Skip to content

Commit cf6d83a

Browse files
committed
first draft; no tests
1 parent 5579937 commit cf6d83a

6 files changed

Lines changed: 150 additions & 25 deletions

File tree

packages/catlog/src/stdlib/analyses/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
pub mod ode;
55

66
pub mod reachability;
7+
pub mod signed_links_to_signed_cat;

packages/catlog/src/stdlib/analyses/ode/mass_action.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,11 @@ impl PetriNetMassActionAnalysis {
215215
pub struct StockFlowMassActionAnalysis {
216216
/// Object type for stocks.
217217
pub stock_ob_type: TabObType,
218-
/// Morphism type for flows between stocks.
218+
/// Morphism types for flows between stocks.
219219
pub flow_mor_type: TabMorType,
220-
/// Morphism type for positive links from stocks to flows.
220+
/// Morphism types for links for stocks to flows.
221221
pub pos_link_mor_type: TabMorType,
222-
/// Morphism type for negative links from stocks to flows.
222+
/// Morphism types for links for stocks to flows.
223223
pub neg_link_mor_type: TabMorType,
224224
}
225225

@@ -250,25 +250,32 @@ impl StockFlowMassActionAnalysis {
250250
})
251251
.collect();
252252

253-
let mut multiply_for_link = |link: QualifiedName, exponent: i8| {
253+
for link in model.mor_generators_with_type(&self.pos_link_mor_type) {
254254
let dom = model.mor_generator_dom(&link).unwrap_basic();
255255
let path = model.mor_generator_cod(&link).unwrap_tabulated();
256256
let Some(TabEdge::Basic(cod)) = path.only() else {
257257
panic!("Codomain of link should be basic morphism");
258258
};
259259
if let Some(term) = terms.get_mut(&cod) {
260-
let mon: Monomial<_, i8> = [(dom, exponent)].into_iter().collect();
260+
let mon: Monomial<_, i8> = [(dom, 1)].into_iter().collect();
261261
*term = std::mem::take(term) * mon;
262262
} else {
263263
panic!("Codomain of link does not belong to model");
264264
};
265-
};
266-
267-
for link in model.mor_generators_with_type(&self.pos_link_mor_type) {
268-
multiply_for_link(link, 1);
269265
}
266+
270267
for link in model.mor_generators_with_type(&self.neg_link_mor_type) {
271-
multiply_for_link(link, -1);
268+
let dom = model.mor_generator_dom(&link).unwrap_basic();
269+
let path = model.mor_generator_cod(&link).unwrap_tabulated();
270+
let Some(TabEdge::Basic(cod)) = path.only() else {
271+
panic!("Codomain of link should be basic morphism");
272+
};
273+
if let Some(term) = terms.get_mut(&cod) {
274+
let mon: Monomial<_, i8> = [(dom, -1)].into_iter().collect();
275+
*term = std::mem::take(term) * mon;
276+
} else {
277+
panic!("Codomain of link does not belong to model");
278+
};
272279
}
273280

274281
let terms: Vec<_> = terms
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//! Migration for going from categories with signed links to signed categories
2+
// (e.g. from signed stock-flow diagrams to causal loop diagrams)
3+
4+
use crate::dbl::discrete_tabulator::theory::TabMorType;
5+
use crate::dbl::discrete_tabulator::theory::TabObType;
6+
use std::rc::Rc;
7+
8+
use crate::dbl::discrete::model::DiscreteDblModel;
9+
use crate::dbl::discrete_tabulator::model::DiscreteTabModel;
10+
use crate::dbl::model::{FgDblModel, MutDblModel};
11+
use crate::one::category::FgCategory;
12+
use crate::one::path::Path;
13+
use crate::stdlib::theories;
14+
use crate::zero::name;
15+
16+
/** Span-migration for categories with signed links.
17+
*
18+
* We create a CLD from a category with signed links from the query defined on
19+
* objects as
20+
*
21+
* V |-> stock : S | flow : F
22+
* E+ |-> out : F | link : L+
23+
* E- |-> in : F | link : L-
24+
*
25+
* and on morphisms as (...) something that will be written up eventually, but
26+
* in short can be described quite simply in words:
27+
*
28+
* - For each stock, create a vertex
29+
* - For each flow, create a (+,-)-span, where the apex is a new vertex
30+
* corresponding to the flow, and there is a negative arrow to the (vertex
31+
* corresponding to the) source of the flow, and a positive arrow to the
32+
* (vertex corresponding to the) target of the flow
33+
* - For each (signed) link, create an arrow (of the same sign) from the
34+
* (vertex corresponding to the) source stock to the (vertex corresponding
35+
* to the) target flow
36+
*/
37+
pub fn migrate(model: DiscreteTabModel) -> DiscreteDblModel {
38+
let mut migrated_model: DiscreteDblModel =
39+
DiscreteDblModel::new(Rc::new(theories::th_signed_category()));
40+
41+
let stock_type = TabObType::Basic(name("Object"));
42+
let flow_type = TabMorType::Hom(Box::new(stock_type.clone()));
43+
let pos_link_type = TabMorType::Basic(name("Link"));
44+
let neg_link_type = TabMorType::Basic(name("NegativeLink"));
45+
46+
// Create an object for each stock (a "stock-object")
47+
for s in model.ob_generators() {
48+
migrated_model.add_ob(s.clone(), name("Object"));
49+
}
50+
51+
// Create a span for each flow
52+
for f in model.mor_generators_with_type(&flow_type) {
53+
// An object for each flow (a "flow-object")
54+
migrated_model.add_ob(f.clone(), name("Object"));
55+
// A negative link from the flow object to the flow-source object
56+
migrated_model.add_mor(
57+
format!("{}_in", f).as_str().into(),
58+
f.clone(),
59+
model.mor_generator_dom(&f).unwrap_basic(),
60+
Path::Id(name("Object")),
61+
);
62+
// A positive link from the flow object to the flow-target object
63+
migrated_model.add_mor(
64+
format!("{}_in", f).as_str().into(),
65+
f.clone(),
66+
model.mor_generator_cod(&f).unwrap_basic(),
67+
name("Negative").into(),
68+
);
69+
}
70+
71+
// Create a positive arrow for each positive link
72+
for pl in model.mor_generators_with_type(&pos_link_type) {
73+
migrated_model.add_mor(
74+
pl.clone(),
75+
model.mor_generator_dom(&pl).unwrap_basic(),
76+
model.mor_generator_cod(&pl).unwrap_basic(),
77+
Path::Id(name("Object")),
78+
);
79+
}
80+
// Create a negative arrow for each negative link
81+
for nl in model.mor_generators_with_type(&neg_link_type) {
82+
migrated_model.add_mor(
83+
nl.clone(),
84+
model.mor_generator_dom(&nl).unwrap_basic(),
85+
model.mor_generator_cod(&nl).unwrap_basic(),
86+
name("Negative").into(),
87+
);
88+
}
89+
90+
migrated_model
91+
}

packages/catlog/src/stdlib/models.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,44 @@ fn backward_link_of_type(th: Rc<DiscreteTabTheory>, link_type: TabMorType) -> Di
124124
model
125125
}
126126

127+
/// The "walking" backward positive link.
128+
///
129+
/// This is the free category with signed links that has a positive link from
130+
/// the codomain of a morphism back to the morphism itself.
131+
pub fn positive_backward_link(th: Rc<DiscreteTabTheory>) -> DiscreteTabModel {
132+
let ob_type = TabObType::Basic(name("Object"));
133+
let mut model = DiscreteTabModel::new(th.clone());
134+
model.add_ob(name("x"), ob_type.clone());
135+
model.add_ob(name("y"), ob_type.clone());
136+
model.add_mor(name("f"), name("x").into(), name("y").into(), th.hom_type(ob_type));
137+
model.add_mor(
138+
name("link"),
139+
name("y").into(),
140+
model.tabulated_gen(name("f")),
141+
TabMorType::Basic(name("Link")),
142+
);
143+
model
144+
}
145+
146+
/// The "walking" backward negative link.
147+
///
148+
/// This is the free category with signed links that has a negative link from
149+
/// the codomain of a morphism back to the morphism itself.
150+
pub fn negative_backward_link(th: Rc<DiscreteTabTheory>) -> DiscreteTabModel {
151+
let ob_type = TabObType::Basic(name("Object"));
152+
let mut model = DiscreteTabModel::new(th.clone());
153+
model.add_ob(name("x"), ob_type.clone());
154+
model.add_ob(name("y"), ob_type.clone());
155+
model.add_mor(name("f"), name("x").into(), name("y").into(), th.hom_type(ob_type));
156+
model.add_mor(
157+
name("link"),
158+
name("y").into(),
159+
model.tabulated_gen(name("f")),
160+
TabMorType::Basic(name("NegativeLink")),
161+
);
162+
model
163+
}
164+
127165
/// A reaction involving three species, one playing the role of a catalyst.
128166
///
129167
/// A free symmetric monoidal category, viewed as a reaction network.

packages/frontend/src/stdlib/theories.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ stdTheories.add(
88
id: "empty",
99
name: "Informal",
1010
description: "The empty logic, allowing only informal content",
11-
iconLetters: ["I", "n"],
1211
isDefault: true,
1312
group: "Base",
1413
},
@@ -20,7 +19,6 @@ stdTheories.add(
2019
id: "simple-olog",
2120
name: "Olog",
2221
description: "Ontology log, a simple conceptual model",
23-
iconLetters: ["O", "l"],
2422
group: "Knowledge and Data",
2523
},
2624
async () => (await import("./theories/simple-olog")).default,
@@ -31,7 +29,6 @@ stdTheories.add(
3129
id: "simple-schema",
3230
name: "Schema",
3331
description: "Schema for a categorical database",
34-
iconLetters: ["S", "c"],
3532
group: "Knowledge and Data",
3633
},
3734
async () => (await import("./theories/simple-schema")).default,
@@ -42,7 +39,6 @@ stdTheories.add(
4239
id: "petri-net",
4340
name: "Petri net",
4441
description: "Place/transition networks",
45-
iconLetters: ["P", "n"],
4642
group: "Systems",
4743
},
4844
async () => (await import("./theories/petri-net")).default,
@@ -53,7 +49,6 @@ stdTheories.add(
5349
id: "causal-loop",
5450
name: "Causal loop diagram",
5551
description: "Positive and negative causal relationships",
56-
iconLetters: ["C", "l"],
5752
group: "System Dynamics",
5853
},
5954
async () => (await import("./theories/causal-loop")).default,
@@ -64,7 +59,6 @@ stdTheories.add(
6459
id: "causal-loop-delays",
6560
name: "Causal loop diagram with delays",
6661
description: "Causal relationships: positive or negative, fast or slow",
67-
iconLetters: ["C", "d"],
6862
group: "System Dynamics",
6963
},
7064
async () => (await import("./theories/causal-loop-delays")).default,
@@ -75,7 +69,6 @@ stdTheories.add(
7569
id: "indeterminate-causal-loop",
7670
name: "Causal loop diagram with indeterminates",
7771
description: "Positive, negative, and indeterminate causal relationships",
78-
iconLetters: ["C", "i"],
7972
group: "System Dynamics",
8073
},
8174
async () => (await import("./theories/indeterminate-causal-loop")).default,
@@ -86,7 +79,6 @@ stdTheories.add(
8679
id: "primitive-stock-flow",
8780
name: "Stock and flow",
8881
description: "Accumulation (stocks) and change (flows)",
89-
iconLetters: ["S", "F"],
9082
group: "System Dynamics",
9183
},
9284
async () => (await import("./theories/primitive-stock-flow")).default,
@@ -97,7 +89,6 @@ stdTheories.add(
9789
id: "primitive-signed-stock-flow",
9890
name: "Stock and flow with signed links",
9991
description: "Accumulation (stocks) and change (flows), with signed links",
100-
iconLetters: ["S", "F"],
10192
group: "System Dynamics",
10293
},
10394
async () => (await import("./theories/primitive-signed-stock-flow")).default,
@@ -108,7 +99,6 @@ stdTheories.add(
10899
id: "reg-net",
109100
name: "Regulatory network",
110101
description: "Biochemical species that promote or inhibit each other",
111-
iconLetters: ["R", "n"],
112102
group: "Biology",
113103
},
114104
async () => (await import("./theories/reg-net")).default,
@@ -119,7 +109,6 @@ stdTheories.add(
119109
id: "unary-dec",
120110
name: "Discrete exterior calculus (DEC)",
121111
description: "DEC operators on a geometrical space",
122-
iconLetters: ["D", "c"],
123112
group: "Experimental",
124113
},
125114
async () => (await import("./theories/unary-dec")).default,
@@ -130,7 +119,6 @@ stdTheories.add(
130119
id: "power-system",
131120
name: "Power system",
132121
description: "Power systems in the style of PyPSA",
133-
iconLetters: ["P", "s"],
134122
group: "Experimental",
135123
},
136124
async () => (await import("./theories/power-system")).default,

packages/frontend/src/stdlib/theories/primitive-signed-stock-flow.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as analyses from "../analyses";
44
import styles from "../styles.module.css";
55
import svgStyles from "../svg_styles.module.css";
66

7-
export default function createPrimitiveSignedStockFlowTheory(theoryMeta: TheoryMeta): Theory {
7+
export default function createPrimitiveStockFlowTheory(theoryMeta: TheoryMeta): Theory {
88
const thCategorySignedLinks = new ThCategorySignedLinks();
99

1010
return new Theory({
@@ -39,7 +39,7 @@ export default function createPrimitiveSignedStockFlowTheory(theoryMeta: TheoryM
3939
description: "Positive influence of a stock on a flow",
4040
arrowStyle: "plus",
4141
preferUnnamed: true,
42-
shortcut: ["P"],
42+
shortcut: ["L"],
4343
},
4444
{
4545
tag: "MorType",
@@ -48,7 +48,7 @@ export default function createPrimitiveSignedStockFlowTheory(theoryMeta: TheoryM
4848
description: "Negative influence of a stock on a flow",
4949
arrowStyle: "minus",
5050
preferUnnamed: true,
51-
shortcut: ["N"],
51+
shortcut: ["K"],
5252
},
5353
],
5454
modelAnalyses: [

0 commit comments

Comments
 (0)