@@ -1654,33 +1654,35 @@ function validate(rs::ReactionSystem, info::String = "")
1654
1654
end
1655
1655
1656
1656
"""
1657
- complexbalanced (rs::ReactionSystem, rates::Vector)
1657
+ iscomplexbalanced (rs::ReactionSystem, rates::Vector)
1658
1658
1659
1659
Constructively compute whether a network will have complex-balanced equilibrium
1660
- solutions, following the method in [this paper](https://link.springer.com/article/10.1007/s10910-015-0498-2#Sec3).
1660
+ solutions, following the method in [this paper](https://link.springer.com/article/10.1007/s10910-015-0498-2#Sec3). Accepts a map of rates [k1 => 1.0, k2 => 2.0,...]k2
1661
1661
"""
1662
1662
1663
- using MetaGraphs, Combinatorics, LinearAlgebra
1663
+ using Combinatorics, LinearAlgebra
1664
1664
1665
- function complexbalanced (rs:: ReactionSystem , rates:: Vector )
1665
+ function iscomplexbalanced (rs:: ReactionSystem , rates:: Dict{Any, Float64} )
1666
1666
if length (rates) != numparams (rs)
1667
1667
error (" The number of reaction rates must be equal to the number of parameters" )
1668
1668
end
1669
- rxm = Dict ( zip ( reactionparams (rs), rates))
1669
+
1670
1670
sm = speciesmap (rs)
1671
1671
cm = reactioncomplexmap (rs)
1672
1672
complexes, D = reactioncomplexes (rs)
1673
1673
rxns = reactions (rs)
1674
1674
nc = length (complexes); nr = numreactions (rs); nm = numspecies (rs)
1675
1675
1676
+ if ! all (r-> ismassaction (r, rs), rxns) error (" Not mass action" ) end
1677
+
1676
1678
# Construct kinetic matrix, K
1677
1679
K = zeros (nr, nc)
1678
1680
for c in 1 : nc
1679
1681
complex = complexes[c]
1680
1682
for (r, dir) in cm[complex]
1681
1683
rxn = rxns[r]
1682
1684
if dir == - 1
1683
- K[r, c] = rxm [rxn. rate]
1685
+ K[r, c] = rates [rxn. rate]
1684
1686
end
1685
1687
end
1686
1688
end
@@ -1689,93 +1691,122 @@ function complexbalanced(rs::ReactionSystem, rates::Vector)
1689
1691
S = netstoichmat (rs)
1690
1692
1691
1693
# Compute ρ using the matrix-tree theorem
1692
- ρ = zeros (nc )
1693
- lcs = linkageclasses (rs )
1694
- rwg = rateweightedgraph (rs, rates)
1694
+ g = incidencematgraph (rs); R = ratematrix (rs, rates )
1695
+ ρ = matrixtree (g, R )
1696
+ @assert isapprox (L * ρ, zeros (nc), atol = 1e-12 )
1695
1697
1696
- for lc in lcs
1697
- sg, vmap = Graphs. induced_subgraph (rwg, lc)
1698
- ρ_j = matrixtree (sg)
1699
- ρ[lc] = ρ_j
1700
- end
1701
-
1702
1698
# Determine if 1) ρ is positive and 2) D^T Ln ρ lies in the image of S^T
1703
1699
if all (x -> x > 0 , ρ)
1704
1700
img = D' * log .(ρ)
1705
- if rank (S' ) == rank (hcat (S' , img))
1706
- return true
1707
- else
1708
- return false
1709
- end
1701
+ if rank (S' ) == rank (hcat (S' , img)) return true else return false end
1710
1702
else
1711
1703
return false
1712
1704
end
1713
1705
end
1714
1706
1715
- """
1716
- rateweightedgraph(rs::ReactionSystem, rates::Vector)
1717
-
1718
- Generate an annotated reaction complex graph of a reaction system, where the nodes are annotated with the reaction complex they correspond to and the edges are annotated with the reaction they correspond to and the rate of the reaction.
1719
- """
1720
- function rateweightedgraph (rs:: ReactionSystem , rates:: Vector )
1707
+ # """
1708
+ # rateweightedgraph(rs::ReactionSystem, rates::Vector)
1709
+ #
1710
+ # Generate an annotated reaction complex graph of a reaction system, where the nodes are annotated with the reaction complex they correspond to and the edges are annotated with the reaction they correspond to and the rate of the reaction.
1711
+ # """
1712
+ #
1713
+ # function rateweightedgraph(rs::ReactionSystem, rates::Dict{Any, Float64})
1714
+ # if length(rates) != numparams(rs)
1715
+ # error("The number of reaction rates must be equal to the number of parameters")
1716
+ # end
1717
+ #
1718
+ # complexes, D = reactioncomplexes(rs)
1719
+ # rxns = reactions(rs)
1720
+ #
1721
+ # g = incidencematgraph(rs)
1722
+ # rwg = MetaDiGraph(g)
1723
+ #
1724
+ # for v in vertices(rwg)
1725
+ # set_prop!(rwg, v, :complex, complexes[v])
1726
+ # end
1727
+ #
1728
+ # for (i, e) in collect(enumerate(edges(rwg)))
1729
+ # rxn = rxns[i]
1730
+ # set_prop!(rwg, Graphs.src(e), Graphs.dst(e), :reaction, rxn)
1731
+ # set_prop!(rwg, Graphs.src(e), Graphs.dst(e), :rate, rates[rxn.rate])
1732
+ # end
1733
+ #
1734
+ # rwg
1735
+ # end
1736
+
1737
+ function ratematrix (rs:: ReactionSystem , rates:: Dict{Any, Float64} )
1721
1738
if length (rates) != numparams (rs)
1722
1739
error (" The number of reaction rates must be equal to the number of parameters" )
1723
1740
end
1724
- rm = Dict (zip (reactionparams (rs), rates))
1725
1741
1726
1742
complexes, D = reactioncomplexes (rs)
1743
+ n = length (complexes)
1727
1744
rxns = reactions (rs)
1745
+ ratematrix = zeros (n, n)
1728
1746
1729
- g = incidencematgraph (rs )
1730
- rwg = MetaDiGraph (g)
1731
-
1732
- for v in vertices (rwg )
1733
- set_prop! (rwg, v, :complex , complexes[v])
1747
+ for r in 1 : length (rxns )
1748
+ rxn = rxns[r]
1749
+ s = findfirst (x -> x == - 1 , D[:,r])
1750
+ p = findfirst (x -> x == 1 , D[:,r] )
1751
+ ratematrix[s, p] = rates[rxn . rate]
1734
1752
end
1753
+ ratematrix
1754
+ end
1735
1755
1736
- for (i, e) in collect (enumerate (edges (rwg)))
1737
- rxn = rxns[i]
1738
- set_prop! (rwg, Graphs. src (e), Graphs. dst (e), :reaction , rxn)
1739
- set_prop! (rwg, Graphs. src (e), Graphs. dst (e), :rate , rm[rxn. rate])
1756
+ """
1757
+ """
1758
+ function matrixtree (g:: SimpleDiGraph , distmx:: Matrix )
1759
+ n = nv (g)
1760
+ if size (distmx) != (n, n)
1761
+ error (" Size of distance matrix is incorrect" )
1740
1762
end
1741
1763
1742
- rwg
1743
- end
1764
+ π = zeros (n)
1765
+
1766
+ if ! Graphs. is_connected (g)
1767
+ ccs = Graphs. connected_components (g)
1768
+ for cc in ccs
1769
+ sg, vmap = Graphs. induced_subgraph (g, cc)
1770
+ distmx_s = distmx[cc, cc]
1771
+ π_j = matrixtree (sg, distmx_s)
1772
+ π[cc] = π_j
1773
+ end
1774
+ return π
1775
+ end
1744
1776
1745
- function matrixtree (g:: MetaDiGraph )
1746
1777
# generate all spanning trees
1747
- # TODO : implement Winter's algorithm for generating spanning trees
1748
- n = nv (g)
1749
1778
ug = SimpleGraph (SimpleDiGraph (g))
1750
1779
trees = collect (Combinatorics. combinations (collect (edges (ug)), n- 1 ))
1751
1780
trees = SimpleGraph .(trees)
1752
1781
trees = filter! (t-> isempty (Graphs. cycle_basis (t)), trees)
1753
-
1754
- π = zeros (n)
1755
-
1756
- function treeweight (t:: SimpleDiGraph )
1757
- prod = 1
1758
- for e in edges (t)
1759
- rate = Graphs. has_edge (g, Graphs. src (e), Graphs. dst (e)) ? get_prop (g, e, :rate ) : 0
1760
- prod *= rate
1761
- end
1762
- prod
1763
- end
1782
+ # trees = spanningtrees(g)
1764
1783
1765
1784
# constructed rooted trees for every edge, compute sum
1766
1785
for v in 1 : n
1767
1786
rootedTrees = [reverse (Graphs. bfs_tree (t, v, dir= :in )) for t in trees]
1768
- π[v] = sum ([treeweight (t) for t in rootedTrees])
1787
+ π[v] = sum ([treeweight (t, g, distmx ) for t in rootedTrees])
1769
1788
end
1770
1789
1771
1790
# sum the contributions
1772
1791
return π
1773
1792
end
1774
1793
1775
- function massactionrate (rs:: ReactionSystem , rxn_idx:: Int , dir:: Int , conc:: Vector )
1776
- if dir != - 1 && dir != 1
1777
- error (" Direction must be either +1 in the case of production or -1 in the case of consumption" )
1794
+ function treeweight (t:: SimpleDiGraph , g:: SimpleDiGraph , distmx:: Matrix )
1795
+ prod = 1
1796
+ for e in edges (t)
1797
+ s = Graphs. src (e); t = Graphs. dst (e)
1798
+ prod *= distmx[s, t]
1778
1799
end
1800
+ prod
1801
+ end
1802
+
1803
+ # TODO : implement Winter's algorithm for generating spanning trees
1804
+ function spanningtrees (g:: SimpleGraph )
1805
+
1806
+ end
1807
+
1808
+ # Checks if a unit consist of exponents with base 1 (and is this unitless).
1809
+ unitless_exp (u) = istree (u) && (operation (u) == ^ ) && (arguments (u)[1 ] == 1 )
1779
1810
1780
1811
rxn = reactions (rs)[rxn_idx]
1781
1812
sm = speciesmap (rs)
0 commit comments