@@ -82,7 +82,7 @@ impl StochasticMassActionAnalysis {
8282}
8383
8484/// Symbolic parameter in mass-action polynomial system.
85- type Parameter < Id > = Polynomial < Id , f32 , u8 > ;
85+ type Parameter < Id > = Polynomial < Id , f32 , i8 > ;
8686
8787/// Mass-action ODE analysis for Petri nets.
8888///
@@ -110,7 +110,7 @@ impl PetriNetMassActionAnalysis {
110110 pub fn build_system (
111111 & self ,
112112 model : & ModalDblModel ,
113- ) -> PolynomialSystem < QualifiedName , Parameter < QualifiedName > , u8 > {
113+ ) -> PolynomialSystem < QualifiedName , Parameter < QualifiedName > , i8 > {
114114 let mut sys = PolynomialSystem :: new ( ) ;
115115 for ob in model. ob_generators_with_type ( & self . place_ob_type ) {
116116 sys. add_term ( ob, Polynomial :: zero ( ) ) ;
@@ -146,7 +146,7 @@ impl PetriNetMassActionAnalysis {
146146 & self ,
147147 model : & ModalDblModel ,
148148 data : MassActionProblemData ,
149- ) -> ODEAnalysis < NumericalPolynomialSystem < u8 > > {
149+ ) -> ODEAnalysis < NumericalPolynomialSystem < i8 > > {
150150 into_numerical_system ( self . build_system ( model) , data)
151151 }
152152
@@ -215,10 +215,12 @@ impl PetriNetMassActionAnalysis {
215215pub struct StockFlowMassActionAnalysis {
216216 /// Object type for stocks.
217217 pub stock_ob_type : TabObType ,
218- /// Morphism types for flows between stocks.
218+ /// Morphism type for flows between stocks.
219219 pub flow_mor_type : TabMorType ,
220- /// Morphism types for links for stocks to flows.
221- pub link_mor_type : TabMorType ,
220+ /// Morphism type for positive links from stocks to flows.
221+ pub pos_link_mor_type : TabMorType ,
222+ /// Morphism type for negative links from stocks to flows.
223+ pub neg_link_mor_type : TabMorType ,
222224}
223225
224226impl Default for StockFlowMassActionAnalysis {
@@ -228,7 +230,8 @@ impl Default for StockFlowMassActionAnalysis {
228230 Self {
229231 stock_ob_type,
230232 flow_mor_type,
231- link_mor_type : TabMorType :: Basic ( name ( "Link" ) ) ,
233+ pos_link_mor_type : TabMorType :: Basic ( name ( "Link" ) ) ,
234+ neg_link_mor_type : TabMorType :: Basic ( name ( "NegativeLink" ) ) ,
232235 }
233236 }
234237}
@@ -238,26 +241,34 @@ impl StockFlowMassActionAnalysis {
238241 pub fn build_system (
239242 & self ,
240243 model : & DiscreteTabModel ,
241- ) -> PolynomialSystem < QualifiedName , Parameter < QualifiedName > , u8 > {
242- let mut terms: HashMap < QualifiedName , Monomial < QualifiedName , u8 > > = model
244+ ) -> PolynomialSystem < QualifiedName , Parameter < QualifiedName > , i8 > {
245+ let mut terms: HashMap < QualifiedName , Monomial < QualifiedName , i8 > > = model
243246 . mor_generators_with_type ( & self . flow_mor_type )
244247 . map ( |flow| {
245248 let dom = model. mor_generator_dom ( & flow) . unwrap_basic ( ) ;
246249 ( flow, Monomial :: generator ( dom) )
247250 } )
248251 . collect ( ) ;
249252
250- for link in model . mor_generators_with_type ( & self . link_mor_type ) {
253+ let mut multiply_for_link = | link : QualifiedName , exponent : i8 | {
251254 let dom = model. mor_generator_dom ( & link) . unwrap_basic ( ) ;
252255 let path = model. mor_generator_cod ( & link) . unwrap_tabulated ( ) ;
253256 let Some ( TabEdge :: Basic ( cod) ) = path. only ( ) else {
254257 panic ! ( "Codomain of link should be basic morphism" ) ;
255258 } ;
256259 if let Some ( term) = terms. get_mut ( & cod) {
257- * term = std:: mem:: take ( term) * Monomial :: generator ( dom) ;
260+ let mon: Monomial < _ , i8 > = [ ( dom, exponent) ] . into_iter ( ) . collect ( ) ;
261+ * term = std:: mem:: take ( term) * mon;
258262 } else {
259263 panic ! ( "Codomain of link does not belong to model" ) ;
260264 } ;
265+ } ;
266+
267+ for link in model. mor_generators_with_type ( & self . pos_link_mor_type ) {
268+ multiply_for_link ( link, 1 ) ;
269+ }
270+ for link in model. mor_generators_with_type ( & self . neg_link_mor_type ) {
271+ multiply_for_link ( link, -1 ) ;
261272 }
262273
263274 let terms: Vec < _ > = terms
@@ -289,15 +300,15 @@ impl StockFlowMassActionAnalysis {
289300 & self ,
290301 model : & DiscreteTabModel ,
291302 data : MassActionProblemData ,
292- ) -> ODEAnalysis < NumericalPolynomialSystem < u8 > > {
303+ ) -> ODEAnalysis < NumericalPolynomialSystem < i8 > > {
293304 into_numerical_system ( self . build_system ( model) , data)
294305 }
295306}
296307
297308fn into_numerical_system (
298- sys : PolynomialSystem < QualifiedName , Parameter < QualifiedName > , u8 > ,
309+ sys : PolynomialSystem < QualifiedName , Parameter < QualifiedName > , i8 > ,
299310 data : MassActionProblemData ,
300- ) -> ODEAnalysis < NumericalPolynomialSystem < u8 > > {
311+ ) -> ODEAnalysis < NumericalPolynomialSystem < i8 > > {
301312 let ob_index: IndexMap < _ , _ > =
302313 sys. components . keys ( ) . cloned ( ) . enumerate ( ) . map ( |( i, x) | ( x, i) ) . collect ( ) ;
303314 let n = ob_index. len ( ) ;
@@ -335,6 +346,30 @@ mod tests {
335346 expected. assert_eq ( & sys. to_string ( ) ) ;
336347 }
337348
349+ #[ test]
350+ fn positive_backward_link_dynamics ( ) {
351+ let th = Rc :: new ( th_category_signed_links ( ) ) ;
352+ let model = positive_backward_link ( th) ;
353+ let sys = StockFlowMassActionAnalysis :: default ( ) . build_system ( & model) ;
354+ let expected = expect ! ( [ r#"
355+ dx = ((-1) f) x y
356+ dy = f x y
357+ "# ] ) ;
358+ expected. assert_eq ( & sys. to_string ( ) ) ;
359+ }
360+
361+ #[ test]
362+ fn negative_backward_link_dynamics ( ) {
363+ let th = Rc :: new ( th_category_signed_links ( ) ) ;
364+ let model = negative_backward_link ( th) ;
365+ let sys = StockFlowMassActionAnalysis :: default ( ) . build_system ( & model) ;
366+ let expected = expect ! ( [ r#"
367+ dx = ((-1) f) x y^{-1}
368+ dy = f x y^{-1}
369+ "# ] ) ;
370+ expected. assert_eq ( & sys. to_string ( ) ) ;
371+ }
372+
338373 #[ test]
339374 fn catalysis_dynamics ( ) {
340375 let th = Rc :: new ( th_sym_monoidal_category ( ) ) ;
0 commit comments