4
4
function filter_constspecs (specs, stoich:: AbstractVector{V} , smap) where {V <: Integer }
5
5
isempty (specs) && (return Vector {Int} (), Vector {V} ())
6
6
7
+ # If any species are constant, go through these manually and add their indices and
8
+ # stoichiometries to `ids` and `filtered_stoich`.
7
9
if any (isconstant, specs)
8
10
ids = Vector {Int} ()
9
11
filtered_stoich = Vector {V} ()
@@ -44,11 +46,15 @@ function reactioncomplexmap(rn::ReactionSystem)
44
46
! isempty (nps. complextorxsmap) && return nps. complextorxsmap
45
47
complextorxsmap = nps. complextorxsmap
46
48
49
+ # Retrieves system reactions and a map from species to their index in the species vector.
47
50
rxs = reactions (rn)
48
51
smap = speciesmap (rn)
49
52
numreactions (rn) > 0 ||
50
53
error (" There must be at least one reaction to find reaction complexes." )
54
+
51
55
for (i, rx) in enumerate (rxs)
56
+ # Create the `ReactionComplex` corresponding to the reaction's substrates. Adds it
57
+ # to the reaction complex dictionary (recording it as the substrates of the i'th reaction).
52
58
subids, substoich = filter_constspecs (rx. substrates, rx. substoich, smap)
53
59
subrc = sort! (ReactionComplex (subids, substoich))
54
60
if haskey (complextorxsmap, subrc)
@@ -57,6 +63,8 @@ function reactioncomplexmap(rn::ReactionSystem)
57
63
complextorxsmap[subrc] = [i => - 1 ]
58
64
end
59
65
66
+ # Create the `ReactionComplex` corresponding to the reaction's products. Adds it
67
+ # to the reaction complex dictionary (recording it as the products of the i'th reaction).
60
68
prodids, prodstoich = filter_constspecs (rx. products, rx. prodstoich, smap)
61
69
prodrc = sort! (ReactionComplex (prodids, prodstoich))
62
70
if haskey (complextorxsmap, prodrc)
@@ -94,7 +102,10 @@ function reactioncomplexes(rn::ReactionSystem; sparse = false)
94
102
isempty (get_systems (rn)) ||
95
103
error (" reactioncomplexes does not currently support subsystems." )
96
104
nps = get_networkproperties (rn)
105
+
106
+ # If the complexes have not been cached, or the cached complexes uses a different sparsity.
97
107
if isempty (nps. complexes) || (sparse != issparse (nps. complexes))
108
+ # Computes the reaction complex dictionary. Use it to create a sparse/dense matrix.
98
109
complextorxsmap = reactioncomplexmap (rn)
99
110
nps. complexes, nps. incidencemat = if sparse
100
111
reactioncomplexes (SparseMatrixCSC{Int, Int}, rn, complextorxsmap)
@@ -105,8 +116,11 @@ function reactioncomplexes(rn::ReactionSystem; sparse = false)
105
116
nps. complexes, nps. incidencemat
106
117
end
107
118
119
+ # Creates a *sparse* reaction complex matrix.
108
120
function reactioncomplexes (:: Type{SparseMatrixCSC{Int, Int}} , rn:: ReactionSystem ,
109
121
complextorxsmap)
122
+ # Computes the I, J, and V vectors used for the sparse matrix (read about sparse matrix
123
+ # representation for more information).
110
124
complexes = collect (keys (complextorxsmap))
111
125
Is = Int[]
112
126
Js = Int[]
@@ -122,6 +136,7 @@ function reactioncomplexes(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem
122
136
complexes, B
123
137
end
124
138
139
+ # Creates a *dense* reaction complex matrix.
125
140
function reactioncomplexes (:: Type{Matrix{Int}} , rn:: ReactionSystem , complextorxsmap)
126
141
complexes = collect (keys (complextorxsmap))
127
142
B = zeros (Int, length (complexes), numreactions (rn))
@@ -158,6 +173,9 @@ function complexstoichmat(rn::ReactionSystem; sparse = false)
158
173
isempty (get_systems (rn)) ||
159
174
error (" complexstoichmat does not currently support subsystems." )
160
175
nps = get_networkproperties (rn)
176
+
177
+ # If the complexes stoichiometry matrix has not been cached, or the cached one uses a
178
+ # different sparsity, computes (and caches) it.
161
179
if isempty (nps. complexstoichmat) || (sparse != issparse (nps. complexstoichmat))
162
180
nps. complexstoichmat = if sparse
163
181
complexstoichmat (SparseMatrixCSC{Int, Int}, rn, keys (reactioncomplexmap (rn)))
@@ -168,7 +186,10 @@ function complexstoichmat(rn::ReactionSystem; sparse = false)
168
186
nps. complexstoichmat
169
187
end
170
188
189
+ # Creates a *sparse* reaction complex stoichiometry matrix.
171
190
function complexstoichmat (:: Type{SparseMatrixCSC{Int, Int}} , rn:: ReactionSystem , rcs)
191
+ # Computes the I, J, and V vectors used for the sparse matrix (read about sparse matrix
192
+ # representation for more information).
172
193
Is = Int[]
173
194
Js = Int[]
174
195
Vs = Int[]
@@ -182,6 +203,7 @@ function complexstoichmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem,
182
203
Z = sparse (Is, Js, Vs, numspecies (rn), length (rcs))
183
204
end
184
205
206
+ # Creates a *dense* reaction complex stoichiometry matrix.
185
207
function complexstoichmat (:: Type{Matrix{Int}} , rn:: ReactionSystem , rcs)
186
208
Z = zeros (Int, numspecies (rn), length (rcs))
187
209
for (i, rc) in enumerate (rcs)
@@ -213,6 +235,9 @@ function complexoutgoingmat(rn::ReactionSystem; sparse = false)
213
235
isempty (get_systems (rn)) ||
214
236
error (" complexoutgoingmat does not currently support subsystems." )
215
237
nps = get_networkproperties (rn)
238
+
239
+ # If the outgoing complexes matrix has not been cached, or the cached one uses a
240
+ # different sparsity, computes (and caches) it.
216
241
if isempty (nps. complexoutgoingmat) || (sparse != issparse (nps. complexoutgoingmat))
217
242
B = reactioncomplexes (rn, sparse = sparse)[2 ]
218
243
nps. complexoutgoingmat = if sparse
@@ -224,16 +249,22 @@ function complexoutgoingmat(rn::ReactionSystem; sparse = false)
224
249
nps. complexoutgoingmat
225
250
end
226
251
252
+ # Creates a *sparse* outgoing reaction complex stoichiometry matrix.
227
253
function complexoutgoingmat (:: Type{SparseMatrixCSC{Int, Int}} , rn:: ReactionSystem , B)
254
+ # Computes the I, J, and V vectors used for the sparse matrix (read about sparse matrix
255
+ # representation for more information).
228
256
n = size (B, 2 )
229
257
rows = rowvals (B)
230
258
vals = nonzeros (B)
231
259
Is = Int[]
232
260
Js = Int[]
233
261
Vs = Int[]
262
+
263
+ # Allocates space to the vectors (so that it is not done incrementally in the loop).
234
264
sizehint! (Is, div (length (vals), 2 ))
235
265
sizehint! (Js, div (length (vals), 2 ))
236
266
sizehint! (Vs, div (length (vals), 2 ))
267
+
237
268
for j in 1 : n
238
269
for i in nzrange (B, j)
239
270
if vals[i] != one (eltype (vals))
@@ -246,6 +277,7 @@ function complexoutgoingmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSyste
246
277
sparse (Is, Js, Vs, size (B, 1 ), size (B, 2 ))
247
278
end
248
279
280
+ # Creates a *dense* outgoing reaction complex stoichiometry matrix.
249
281
function complexoutgoingmat (:: Type{Matrix{Int}} , rn:: ReactionSystem , B)
250
282
Δ = copy (B)
251
283
for (I, b) in pairs (Δ)
@@ -284,10 +316,14 @@ function incidencematgraph(rn::ReactionSystem)
284
316
nps. incidencegraph
285
317
end
286
318
319
+ # Computes the incidence graph from an *dense* incidence matrix.
287
320
function incidencematgraph (incidencemat:: Matrix{Int} )
288
321
@assert all (∈ ([- 1 , 0 , 1 ]), incidencemat)
289
322
n = size (incidencemat, 1 ) # no. of nodes/complexes
290
323
graph = Graphs. DiGraph (n)
324
+
325
+ # Walks through each column (corresponds to reactions). For each, find the input and output
326
+ # complex and add an edge representing these to the incidence graph.
291
327
for col in eachcol (incidencemat)
292
328
src = 0
293
329
dst = 0
@@ -301,12 +337,16 @@ function incidencematgraph(incidencemat::Matrix{Int})
301
337
return graph
302
338
end
303
339
340
+ # Computes the incidence graph from an *sparse* incidence matrix.
304
341
function incidencematgraph (incidencemat:: SparseMatrixCSC{Int, Int} )
305
342
@assert all (∈ ([- 1 , 0 , 1 ]), incidencemat)
306
343
m, n = size (incidencemat)
307
344
graph = Graphs. DiGraph (m)
308
345
rows = rowvals (incidencemat)
309
346
vals = nonzeros (incidencemat)
347
+
348
+ # Loops through the (n) columns. For each column, directly find the index of the input
349
+ # and output complex and add an edge representing these to the incidence graph.
310
350
for j in 1 : n
311
351
inds = nzrange (incidencemat, j)
312
352
row = rows[inds]
@@ -387,32 +427,43 @@ rcs,incidencemat = reactioncomplexes(sir)
387
427
```
388
428
"""
389
429
function deficiency (rn:: ReactionSystem )
430
+ # Precomputes required information. `conservationlaws` caches the conservation laws in `rn`.
390
431
nps = get_networkproperties (rn)
391
432
conservationlaws (rn)
392
433
r = nps. rank
393
434
ig = incidencematgraph (rn)
394
435
lc = linkageclasses (rn)
436
+
437
+ # Computes deficiency using its formula. Caches and returns it as output.
395
438
nps. deficiency = Graphs. nv (ig) - length (lc) - r
396
439
nps. deficiency
397
440
end
398
441
399
- # Used in the subsequent function.
442
+ # For a linkage class (set of reaction complexes that form an isolated sub-graph), and some
443
+ # additional information of the full network, find the reactions, species, and parameters
444
+ # that constitute the corresponding sub-reaction network.
400
445
function subnetworkmapping (linkageclass, allrxs, complextorxsmap, p)
446
+ # Finds the reactions that are part of teh sub-reaction network.
401
447
rxinds = sort! (collect (Set (rxidx for rcidx in linkageclass
402
448
for rxidx in complextorxsmap[rcidx])))
403
- rxs = allrxs[rxinds]
404
- specset = Set (s for rx in rxs for s in rx. substrates if ! isconstant (s))
405
- for rx in rxs
449
+ newrxs = allrxs[rxinds]
450
+
451
+ # Find the species that are part of the sub-reaction network.
452
+ specset = Set (s for rx in newrxs for s in rx. substrates if ! isconstant (s))
453
+ for rx in newrxs
406
454
for product in rx. products
407
455
! isconstant (product) && push! (specset, product)
408
456
end
409
457
end
410
- specs = collect (specset)
458
+ newspecs = collect (specset)
459
+
460
+ # Find the parameters that are part of the sub-reaction network.
411
461
newps = Vector {eltype(p)} ()
412
- for rx in rxs
462
+ for rx in newrxs
413
463
Symbolics. get_variables! (newps, rx. rate, p)
414
464
end
415
- rxs, specs, newps # reactions and species involved in reactions of subnetwork
465
+
466
+ newrxs, newspecs, newps
416
467
end
417
468
418
469
"""
@@ -436,18 +487,23 @@ subnetworks(sir)
436
487
"""
437
488
function subnetworks (rs:: ReactionSystem )
438
489
isempty (get_systems (rs)) || error (" subnetworks does not currently support subsystems." )
490
+
491
+ # Retrieves required components. `linkageclasses` caches linkage classes in `rs`.
439
492
lcs = linkageclasses (rs)
440
493
rxs = reactions (rs)
441
494
p = parameters (rs)
442
495
t = get_iv (rs)
443
496
spatial_ivs = get_sivs (rs)
444
497
complextorxsmap = [map (first, rcmap) for rcmap in values (reactioncomplexmap (rs))]
445
498
subnetworks = Vector {ReactionSystem} ()
499
+
500
+ # Loops through each sub-graph of connected reaction complexes. For each, create a
501
+ # new `ReactionSystem` model and pushes it to the subnetworks vector.
446
502
for i in 1 : length (lcs)
447
- reacs, specs , newps = subnetworkmapping (lcs[i], rxs, complextorxsmap, p)
503
+ newrxs, newspecs , newps = subnetworkmapping (lcs[i], rxs, complextorxsmap, p)
448
504
newname = Symbol (nameof (rs), " _" , i)
449
505
push! (subnetworks,
450
- ReactionSystem (reacs , t, specs , newps; name = newname, spatial_ivs))
506
+ ReactionSystem (newrxs , t, newspecs , newps; name = newname, spatial_ivs))
451
507
end
452
508
subnetworks
453
509
end
@@ -475,6 +531,9 @@ function linkagedeficiencies(rs::ReactionSystem)
475
531
lcs = linkageclasses (rs)
476
532
subnets = subnetworks (rs)
477
533
δ = zeros (Int, length (lcs))
534
+
535
+ # For each sub-reaction network of the reaction network, compute its deficiency. Returns
536
+ # the full vector of deficiencies for each sub-reaction network.
478
537
for (i, subnet) in enumerate (subnets)
479
538
conservationlaws (subnet)
480
539
nps = get_networkproperties (subnet)
@@ -531,11 +590,16 @@ function isweaklyreversible(rn::ReactionSystem, subnets)
531
590
im = get_networkproperties (rn). incidencemat
532
591
isempty (im) &&
533
592
error (" Error, please call reactioncomplexes(rn::ReactionSystem) to ensure the incidence matrix has been cached." )
593
+
594
+ # For each sub-reaction network, caches its reaction complexes.
534
595
sparseig = issparse (im)
535
596
for subnet in subnets
536
597
nps = get_networkproperties (subnet)
537
598
isempty (nps. incidencemat) && reactioncomplexes (subnet; sparse = sparseig)
538
599
end
600
+
601
+ # the network is weakly reversible if each sub-network's incidenc graph is strongl
602
+ # connec (i.e. each node is reachable from each node).
539
603
all (Graphs. is_strongly_connected ∘ incidencematgraph, subnets)
540
604
end
541
605
@@ -645,25 +709,43 @@ end
645
709
646
710
# Used in the subsequent function.
647
711
function cache_conservationlaw_eqs! (rn:: ReactionSystem , N:: AbstractMatrix , col_order)
712
+ # Retrieves nullity (the number of conservation laws). `r` is the rank of the netstoichmat.
648
713
nullity = size (N, 1 )
649
- r = numspecies (rn) - nullity # rank of the netstoichmat
650
- sts = species (rn)
714
+ r = numspecies (rn) - nullity
715
+
716
+ # Creates vectors of all independent and dependent species (those that are not, and are,
717
+ # eliminated by the conservation laws). Get vectors both with their indexes and the actual
718
+ # species symbolic variables.
719
+ sps = species (rn)
651
720
indepidxs = col_order[begin : r]
652
- indepspecs = sts [indepidxs]
721
+ indepspecs = sps [indepidxs]
653
722
depidxs = col_order[(r + 1 ): end ]
654
- depspecs = sts[depidxs]
723
+ depspecs = sps[depidxs]
724
+
725
+ # Declares the conservation law parameters.
655
726
constants = MT. unwrap .(MT. scalarize (only (
656
727
@parameters $ (CONSERVED_CONSTANT_SYMBOL)[1 : nullity] [conserved= true ])))
657
728
729
+ # Computes the equations for (examples uses simple two-state system, `X1 <--> X2`):
730
+ # - The species eliminated through conservation laws (`conservedeqs`). E.g. `[X2 ~ X1 - Γ[1]]`.
731
+ # - The conserved quantity parameters (`constantdefs`). E.g. `[Γ[1] ~ X1 + X2]`.
658
732
conservedeqs = Equation[]
659
733
constantdefs = Equation[]
734
+
735
+ # For each conserved quantity.
660
736
for (i, depidx) in enumerate (depidxs)
737
+ # Finds the coefficient (in the conservation law) of the species that is eliminated
738
+ # by this conservation law.
661
739
scaleby = (N[i, depidx] != 1 ) ? N[i, depidx] : one (eltype (N))
662
740
(scaleby != 0 ) || error (" Error, found a zero in the conservation law matrix where "
663
- *
664
- " one was not expected." )
741
+ * " one was not expected." )
742
+
743
+ # Creates, for this conservation law, the sum of all independent species (weighted by
744
+ # the ratio between the coefficient of the species and the species which is elimianted.
665
745
coefs = @view N[i, indepidxs]
666
- terms = sum (p -> p[1 ] / scaleby * p[2 ], zip (coefs, indepspecs))
746
+ terms = sum ((coef, sp) -> coef / scaleby * sp, zip (coefs, indepspecs))
747
+
748
+ # Computes the two equations corresponding to this conserved quantity.
667
749
eq = depspecs[i] ~ constants[i] - terms
668
750
push! (conservedeqs, eq)
669
751
eq = constants[i] ~ depspecs[i] + terms
@@ -695,6 +777,8 @@ Notes:
695
777
function conservationlaws (rs:: ReactionSystem )
696
778
nps = get_networkproperties (rs)
697
779
! isempty (nps. conservationmat) && (return nps. conservationmat)
780
+
781
+ # If the conservation law matrix is not computed, do so and caches the result.
698
782
nsm = netstoichmat (rs)
699
783
nps. conservationmat = conservationlaws (nsm; col_order = nps. col_order)
700
784
cache_conservationlaw_eqs! (rs, nps. conservationmat, nps. col_order)
@@ -708,13 +792,13 @@ Compute conserved quantities for a system with the given conservation laws.
708
792
"""
709
793
conservedquantities (state, cons_laws) = cons_laws * state
710
794
711
- # If u0s are not given while conservation laws are present, throws an error.
795
+ # If u0s are not given while conservation laws are present, throw an error.
712
796
# Used in HomotopyContinuation and BifurcationKit extensions.
713
- # Currently only checks if any u0s are given
714
- # (not whether these are enough for computing conserved quantitites , this will yield a less informative error).
797
+ # Currently only checks if any u0s are given (not whether these are enough for computing
798
+ # conserved quantities , this will yield a less informative error).
715
799
function conservationlaw_errorcheck (rs, pre_varmap)
716
800
vars_with_vals = Set (p[1 ] for p in pre_varmap)
717
- any (s -> s in vars_with_vals, species (rs)) && return
801
+ any (sp -> sp in vars_with_vals, species (rs)) && return
718
802
isempty (conservedequations (Catalyst. flatten (rs))) ||
719
803
error (" The system has conservation laws but initial conditions were not provided for some species." )
720
804
end
0 commit comments