Skip to content

Commit d854bfd

Browse files
committed
init
1 parent e357827 commit d854bfd

File tree

2 files changed

+81
-46
lines changed

2 files changed

+81
-46
lines changed

ext/CatalystGraphMakieExtension.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
module CatalystGraphMakieExtension
22

33
# Fetch packages.
4-
using Catalyst, GraphMakie, Graphs, Symbolics
4+
using Catalyst, GraphMakie, Graphs, Symbolics, SparseArrays
55
using Symbolics: get_variables!
66
import Catalyst: species_reaction_graph, incidencematgraph, lattice_plot, lattice_animation
7+
import SparseArrays: rowvals, nonzeros, nzrange
78

89
# Creates and exports graph plotting functions.
910
include("CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl")

ext/CatalystGraphMakieExtension/rn_graph_plot.jl

Lines changed: 79 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,22 @@
33
#############
44

55
"""
6-
SRGraphWrap{T}
6+
MultiGraphWrap{T}
77
8-
Wrapper for the species-reaction graph containing edges for rate-dependence on species. Intended to allow plotting of multiple edges.
8+
Wrapper intended to allow plotting of multiple edges. This is needed in the following cases:
9+
- For the species-reaction graph, multiple edges can exist when a reaction depends on some species for its rate, and if that species is produced by the reaction.
10+
- For the complex graph, multiple edges can exist between a pair of nodes if there are multiple reactions between the same complexes. This might include a reversible pair of reactions - we might have three total edges if one reaction is reversible, and we have a separate reaction going from one complex to the other.
11+
12+
`gen_distances` sets the distances between the edges that allows multiple to be visible on the plot at the same time.
913
"""
10-
struct SRGraphWrap{T} <: Graphs.AbstractGraph{T}
14+
struct MultiGraphWrap{T} <: Graphs.AbstractGraph{T}
1115
g::SimpleDiGraph{T}
12-
rateedges::Vector{Graphs.SimpleEdge{T}}
16+
multiedges::Vector{Graphs.SimpleEdge{T}}
1317
edgeorder::Vector{Int64}
1418
end
1519

16-
# Create the SimpleDiGraph corresponding to the species and reactions
17-
function SRGraphWrap(rn::ReactionSystem)
20+
# Create the SimpleDiGraph corresponding to the species and reactions, the species-reaction graph
21+
function MultiGraphWrap(rn::ReactionSystem)
1822
srg = species_reaction_graph(rn)
1923
rateedges = Vector{Graphs.SimpleEdge{Int}}()
2024
sm = speciesmap(rn); specs = species(rn)
@@ -32,33 +36,46 @@ function SRGraphWrap(rn::ReactionSystem)
3236
end
3337
edgelist = vcat(collect(Graphs.edges(srg)), rateedges)
3438
edgeorder = sortperm(edgelist)
35-
SRGraphWrap(srg, rateedges, edgeorder)
39+
MultiGraphWrap(srg, rateedges, edgeorder)
40+
end
41+
42+
# Automatically set edge order if not supplied
43+
function MultiGraphWrap(g::SimpleDiGraph{T}, multiedges::Vector{Graphs.SimpleEdge{T}}) where T
44+
edgelist = vcat(collect(Graphs.edges(g)), multiedges)
45+
edgeorder = sortperm(edgelist)
46+
MultiGraphWrap(g, multiedges, edgeorder)
3647
end
3748

38-
Base.eltype(g::SRGraphWrap) = eltype(g.g)
39-
Graphs.edgetype(g::SRGraphWrap) = edgetype(g.g)
40-
Graphs.has_edge(g::SRGraphWrap, s, d) = has_edge(g.g, s, d)
41-
Graphs.has_vertex(g::SRGraphWrap, i) = has_vertex(g.g, i)
42-
Graphs.inneighbors(g::SRGraphWrap{T}, i) where T = inneighbors(g.g, i)
43-
Graphs.outneighbors(g::SRGraphWrap{T}, i) where T = outneighbors(g.g, i)
44-
Graphs.ne(g::SRGraphWrap) = length(g.rateedges) + length(Graphs.edges(g.g))
45-
Graphs.nv(g::SRGraphWrap) = nv(g.g)
46-
Graphs.vertices(g::SRGraphWrap) = vertices(g.g)
47-
Graphs.is_directed(g::SRGraphWrap) = is_directed(g.g)
48-
49-
function Graphs.edges(g::SRGraphWrap)
50-
edgelist = vcat(collect(Graphs.edges(g.g)), g.rateedges)[g.edgeorder]
49+
Base.eltype(g::MultiGraphWrap) = eltype(g.g)
50+
Graphs.edgetype(g::MultiGraphWrap) = edgetype(g.g)
51+
Graphs.has_edge(g::MultiGraphWrap, s, d) = has_edge(g.g, s, d)
52+
Graphs.has_vertex(g::MultiGraphWrap, i) = has_vertex(g.g, i)
53+
Graphs.inneighbors(g::MultiGraphWrap{T}, i) where T = inneighbors(g.g, i)
54+
Graphs.outneighbors(g::MultiGraphWrap{T}, i) where T = outneighbors(g.g, i)
55+
Graphs.ne(g::MultiGraphWrap) = length(g.multiedges) + length(Graphs.edges(g.g))
56+
Graphs.nv(g::MultiGraphWrap) = nv(g.g)
57+
Graphs.vertices(g::MultiGraphWrap) = vertices(g.g)
58+
Graphs.is_directed(::Type{<:MultiGraphWrap}) = true
59+
Graphs.is_directed(g::MultiGraphWrap) = is_directed(g.g)
60+
61+
function Graphs.edges(g::MultiGraphWrap)
62+
edgelist = vcat(collect(Graphs.edges(g.g)), g.multiedges)[g.edgeorder]
5163
end
5264

53-
function gen_distances(g::SRGraphWrap; inc = 0.2)
65+
function gen_distances(g::MultiGraphWrap; inc = 0.25)
5466
edgelist = edges(g)
5567
distances = zeros(length(edgelist))
56-
for i in 2:Base.length(edgelist)
57-
edgelist[i] == edgelist[i-1] && (distances[i] = inc)
68+
for (i,e) in enumerate(edgelist)
69+
fwd = findall(==(e), edgelist); rev = findall(==(reverse(e)), edgelist)
70+
distances[i] != 0 && continue
71+
distances[fwd] = collect(0:inc:inc*(length(fwd)-1))
72+
distances[rev] = collect(inc:inc:inc*(length(rev)))
5873
end
5974
distances
6075
end
6176

77+
Base.copy(g::MultiGraphWrap) = MultiGraphWrap(g.g, g.multiedges, g.edgeorder)
78+
6279
"""
6380
plot_network(rn::ReactionSystem)
6481
@@ -77,7 +94,7 @@ Notes:
7794
red and black arrows from `A` to the reaction node.
7895
"""
7996
function Catalyst.plot_network(rn::ReactionSystem)
80-
srg = SRGraphWrap(rn)
97+
srg = MultiGraphWrap(rn)
8198
ns = length(species(rn))
8299
nodecolors = vcat([:skyblue3 for i in 1:ns],
83100
[:green for i in ns+1:nv(srg)])
@@ -104,16 +121,16 @@ function Catalyst.plot_network(rn::ReactionSystem)
104121
end
105122

106123
graphplot(srg;
107-
edge_color = edgecolors,
108-
elabels = edgelabels,
109-
elabels_rotation = 0,
110-
ilabels = ilabels,
111-
node_color = nodecolors,
112-
node_size = nodesizes,
113-
arrow_shift = :end,
114-
arrow_size = 20,
115-
curve_distance_usage = true,
116-
curve_distance = gen_distances(srg)
124+
edge_color = edgecolors,
125+
elabels = edgelabels,
126+
elabels_rotation = 0,
127+
ilabels = ilabels,
128+
node_color = nodecolors,
129+
node_size = nodesizes,
130+
arrow_shift = :end,
131+
arrow_size = 20,
132+
curve_distance_usage = true,
133+
curve_distance = gen_distances(srg),
117134
)
118135
end
119136

@@ -130,27 +147,44 @@ end
130147
depends on species. i.e. `k*C, A --> B` for `C` a species.
131148
"""
132149
function Catalyst.plot_complexes(rn::ReactionSystem)
133-
img = incidencematgraph(rn)
150+
img = incidencematgraph(rn); D = incidencemat(rn; sparse=true)
134151
specs = species(rn); rxs = reactions(rn)
135-
edgecolors = [:black for i in 1:ne(img)]
136-
nodelabels = complexlabels(rn)
152+
edgecolors = [:black for i in 1:length(rxs)]
137153
edgelabels = [repr(rx.rate) for rx in rxs]
138154

139155
deps = Set()
156+
edgelist = Vector{Graphs.SimpleEdge{Int}}()
157+
rows = SparseArrays.rowvals(D); vals = SparseArrays.nonzeros(D)
158+
159+
# Construct the edge order for reactions.
140160
for (i, rx) in enumerate(rxs)
161+
inds = SparseArrays.nzrange(D, i)
162+
val = vals[inds]; row = rows[inds]
163+
(sub, prod) = val[1] == -1 ? (row[1], row[2]) : (row[2], row[1])
164+
push!(edgelist, Graphs.SimpleEdge(sub, prod))
165+
141166
empty!(deps)
142167
get_variables!(deps, rx.rate, specs)
143168
(!isempty(deps)) && (edgecolors[i] = :red)
144169
end
145170

146-
graphplot(img;
147-
edge_color = edgecolors,
148-
elabels = edgelabels,
149-
ilabels = complexlabels(rn),
150-
node_color = :skyblue3,
151-
elabels_rotation = 0,
152-
arrow_shift = :end,
153-
curve_distance = 0.2
171+
# Resolve differences between reaction order and edge order in the incidencematgraph.
172+
rxorder = sortperm(edgelist); edgelist = edgelist[rxorder]
173+
multiedges = Vector{Graphs.SimpleEdge{Int}}()
174+
for i in 2:length(edgelist)
175+
isequal(edgelist[i], edgelist[i-1]) && push!(multiedges, edgelist[i])
176+
end
177+
img_ = MultiGraphWrap(img, multiedges)
178+
179+
graphplot(img_;
180+
edge_color = edgecolors[rxorder],
181+
elabels = edgelabels[rxorder],
182+
ilabels = complexlabels(rn),
183+
node_color = :skyblue3,
184+
elabels_rotation = 0,
185+
arrow_shift = :end,
186+
curve_distance_usage = true,
187+
curve_distance = gen_distances(img_)
154188
)
155189
end
156190

0 commit comments

Comments
 (0)