1use std::sync::Arc;
8
9use ustr::ustr;
10use wasm_bindgen::prelude::*;
11
12use catlog::dbl::{model, theory};
13use catlog::one::fin_category::FinMor;
14use catlog::stdlib::{analyses, models, theories};
15
16use super::model_morphism::{MotifsOptions, motifs};
17use super::{analyses::*, model::DblModel, theory::DblTheory};
18
19#[wasm_bindgen]
21pub struct ThEmpty(Arc<theory::UstrDiscreteDblTheory>);
22
23#[wasm_bindgen]
24impl ThEmpty {
25 #[wasm_bindgen(constructor)]
26 pub fn new() -> Self {
27 Self(Arc::new(theories::th_empty()))
28 }
29
30 #[wasm_bindgen]
31 pub fn theory(&self) -> DblTheory {
32 DblTheory(self.0.clone().into())
33 }
34}
35
36#[wasm_bindgen]
38pub struct ThCategory(Arc<theory::UstrDiscreteDblTheory>);
39
40#[wasm_bindgen]
41impl ThCategory {
42 #[wasm_bindgen(constructor)]
43 pub fn new() -> Self {
44 Self(Arc::new(theories::th_category()))
45 }
46
47 #[wasm_bindgen]
48 pub fn theory(&self) -> DblTheory {
49 DblTheory(self.0.clone().into())
50 }
51}
52
53#[wasm_bindgen]
55pub struct ThSchema(Arc<theory::UstrDiscreteDblTheory>);
56
57#[wasm_bindgen]
58impl ThSchema {
59 #[wasm_bindgen(constructor)]
60 pub fn new() -> Self {
61 Self(Arc::new(theories::th_schema()))
62 }
63
64 #[wasm_bindgen]
65 pub fn theory(&self) -> DblTheory {
66 DblTheory(self.0.clone().into())
67 }
68}
69
70#[wasm_bindgen]
72pub struct ThSignedCategory(Arc<theory::UstrDiscreteDblTheory>);
73
74#[wasm_bindgen]
75impl ThSignedCategory {
76 #[wasm_bindgen(constructor)]
77 pub fn new() -> Self {
78 Self(Arc::new(theories::th_signed_category()))
79 }
80
81 #[wasm_bindgen]
82 pub fn theory(&self) -> DblTheory {
83 DblTheory(self.0.clone().into())
84 }
85
86 #[wasm_bindgen(js_name = "positiveLoops")]
88 pub fn positive_loops(
89 &self,
90 model: &DblModel,
91 options: MotifsOptions,
92 ) -> Result<Vec<DblModel>, String> {
93 let positive_loop = models::positive_loop(self.0.clone());
94 motifs(&positive_loop, model, options)
95 }
96
97 #[wasm_bindgen(js_name = "negativeLoops")]
99 pub fn negative_loops(
100 &self,
101 model: &DblModel,
102 options: MotifsOptions,
103 ) -> Result<Vec<DblModel>, String> {
104 let negative_loop = models::negative_loop(self.0.clone());
105 motifs(&negative_loop, model, options)
106 }
107
108 #[wasm_bindgen(js_name = "lotkaVolterra")]
110 pub fn lotka_volterra(
111 &self,
112 model: &DblModel,
113 data: LotkaVolterraModelData,
114 ) -> Result<ODEResult, String> {
115 let model: &model::DiscreteDblModel<_, _> = (&model.0)
116 .try_into()
117 .map_err(|_| "Lotka-Volterra simulation expects a discrete double model")?;
118 Ok(ODEResult(
119 analyses::ode::LotkaVolterraAnalysis::new(ustr("Object"))
120 .add_positive(FinMor::Id(ustr("Object")))
121 .add_negative(FinMor::Generator(ustr("Negative")))
122 .create_system(model, data.0)
123 .solve_with_defaults()
124 .map_err(|err| format!("{:?}", err))
125 .into(),
126 ))
127 }
128}
129
130#[wasm_bindgen]
132pub struct ThDelayableSignedCategory(Arc<theory::UstrDiscreteDblTheory>);
133
134#[wasm_bindgen]
135impl ThDelayableSignedCategory {
136 #[wasm_bindgen(constructor)]
137 pub fn new() -> Self {
138 Self(Arc::new(theories::th_delayable_signed_category()))
139 }
140
141 #[wasm_bindgen]
142 pub fn theory(&self) -> DblTheory {
143 DblTheory(self.0.clone().into())
144 }
145
146 #[wasm_bindgen(js_name = "positiveLoops")]
148 pub fn positive_loops(
149 &self,
150 model: &DblModel,
151 options: MotifsOptions,
152 ) -> Result<Vec<DblModel>, String> {
153 let positive_loop = models::positive_loop(self.0.clone());
154 motifs(&positive_loop, model, options)
155 }
156
157 #[wasm_bindgen(js_name = "negativeLoops")]
159 pub fn negative_loops(
160 &self,
161 model: &DblModel,
162 options: MotifsOptions,
163 ) -> Result<Vec<DblModel>, String> {
164 let negative_loop = models::negative_loop(self.0.clone());
165 motifs(&negative_loop, model, options)
166 }
167
168 #[wasm_bindgen(js_name = "delayedPositiveLoops")]
170 pub fn delayed_positive_loops(
171 &self,
172 model: &DblModel,
173 options: MotifsOptions,
174 ) -> Result<Vec<DblModel>, String> {
175 let delayed_positive_loop = models::delayed_positive_loop(self.0.clone());
176 motifs(&delayed_positive_loop, model, options)
177 }
178
179 #[wasm_bindgen(js_name = "delayedNegativeLoops")]
181 pub fn delayed_negative_loops(
182 &self,
183 model: &DblModel,
184 options: MotifsOptions,
185 ) -> Result<Vec<DblModel>, String> {
186 let delayed_negative_loop = models::delayed_negative_loop(self.0.clone());
187 motifs(&delayed_negative_loop, model, options)
188 }
189}
190
191#[wasm_bindgen]
193pub struct ThNullableSignedCategory(Arc<theory::UstrDiscreteDblTheory>);
194
195#[wasm_bindgen]
196impl ThNullableSignedCategory {
197 #[wasm_bindgen(constructor)]
198 pub fn new() -> Self {
199 Self(Arc::new(theories::th_nullable_signed_category()))
200 }
201
202 #[wasm_bindgen]
203 pub fn theory(&self) -> DblTheory {
204 DblTheory(self.0.clone().into())
205 }
206}
207
208#[wasm_bindgen]
210pub struct ThCategoryWithScalars(Arc<theory::UstrDiscreteDblTheory>);
211
212#[wasm_bindgen]
213impl ThCategoryWithScalars {
214 #[wasm_bindgen(constructor)]
215 pub fn new() -> Self {
216 Self(Arc::new(theories::th_category_with_scalars()))
217 }
218
219 #[wasm_bindgen]
220 pub fn theory(&self) -> DblTheory {
221 DblTheory(self.0.clone().into())
222 }
223}
224
225#[wasm_bindgen]
227pub struct ThCategoryLinks(Arc<theory::UstrDiscreteTabTheory>);
228
229#[wasm_bindgen]
230impl ThCategoryLinks {
231 #[wasm_bindgen(constructor)]
232 pub fn new() -> Self {
233 Self(Arc::new(theories::th_category_links()))
234 }
235
236 #[wasm_bindgen]
237 pub fn theory(&self) -> DblTheory {
238 DblTheory(self.0.clone().into())
239 }
240
241 #[wasm_bindgen(js_name = "massAction")]
243 pub fn mass_action(
244 &self,
245 model: &DblModel,
246 data: MassActionModelData,
247 ) -> Result<ODEResult, String> {
248 let model: &model::DiscreteTabModel<_, _, _> = (&model.0)
249 .try_into()
250 .map_err(|_| "Mass-action simulation expects a discrete tabulator model")?;
251 Ok(ODEResult(
252 analyses::ode::StockFlowMassActionAnalysis::default()
253 .create_numerical_system(model, data.0)
254 .solve_with_defaults()
255 .map_err(|err| format!("{:?}", err))
256 .into(),
257 ))
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264 use crate::theory::*;
265 use ustr::ustr;
266
267 #[test]
268 fn discrete_dbl_theory() {
269 let th = ThSchema::new().theory();
270 let entity = ObType::Basic(ustr("Entity"));
271 let attr_type = ObType::Basic(ustr("AttrType"));
272 let attr = MorType::Basic(ustr("Attr"));
273 assert_eq!(th.src(attr.clone()), Ok(entity));
274 assert_eq!(th.tgt(attr), Ok(attr_type));
275 }
276
277 #[test]
278 fn discrete_tab_theory() {
279 let th = ThCategoryLinks::new().theory();
280 let x = ObType::Basic(ustr("Object"));
281 let link = MorType::Basic(ustr("Link"));
282 assert_eq!(th.src(link.clone()), Ok(x));
283 assert!(matches!(th.tgt(link), Ok(ObType::Tabulator(_))));
284 }
285}