Skip to content

Commit

Permalink
Merge pull request #1130 from vyudu/graph-docs
Browse files Browse the repository at this point in the history
GraphMakie docs update
  • Loading branch information
vyudu authored Jan 9, 2025
2 parents 51b9d0b + 05fbd4f commit 494ee45
Show file tree
Hide file tree
Showing 18 changed files with 164 additions and 564 deletions.
13 changes: 12 additions & 1 deletion HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,18 @@
(k1, k2), A <--> B
end
```

- Catalyst's network visualization capability has shifted from using Graphviz to [GraphMakie.jl](https://graph.makie.org/stable/). To use this functionality, load the GraphMakie extension by installing `Catalyst` and `GraphMakie`, along with a Makie backend like `GLMakie`. There are two new methods for visualizing graphs: `plot_network` and `plot_complexes`, which respectively display the species-reaction graph and complex graph.
```julia
using Catalyst, GraphMakie, GLMakie
brusselator = @reaction_network begin
A, ∅ --> X
1, 2X + Y --> 3X
B, X --> Y
1, X -->
end
plot_network(brusselator)
```

## Catalyst 14.4.1
- Support for user-defined functions on the RHS when providing coupled equations
for CRNs using the @equations macro. For example, the following now works:
Expand Down
3 changes: 1 addition & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ julia = "1.10"
[extras]
DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def"
DomainSets = "5b8099bc-c8ec-5219-889f-1d9e522a28bf"
Graphviz_jll = "3c863552-8265-54e4-a6dc-903eb78fde85"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
Expand All @@ -99,4 +98,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[targets]
test = ["DiffEqCallbacks", "DomainSets", "Graphviz_jll", "Logging", "NonlinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqDefault", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Pkg", "Plots", "Random", "SafeTestsets", "SciMLBase", "SciMLNLSolve", "StableRNGs", "StaticArrays", "Statistics", "SteadyStateDiffEq", "StochasticDiffEq", "Test", "Unitful"]
test = ["DiffEqCallbacks", "DomainSets", "Logging", "NonlinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqDefault", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Pkg", "Plots", "Random", "SafeTestsets", "SciMLBase", "SciMLNLSolve", "StableRNGs", "StaticArrays", "Statistics", "SteadyStateDiffEq", "StochasticDiffEq", "Test", "Unitful"]
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a"
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
Expand Down
6 changes: 5 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Documenter
using Catalyst, ModelingToolkit
# Add packages for plotting
using GraphMakie, CairoMakie

docpath = Base.source_dir()
assetpath = joinpath(docpath, "src", "assets")
Expand Down Expand Up @@ -37,7 +39,9 @@ makedocs(sitename = "Catalyst.jl",
collapselevel = 1,
assets = ["assets/favicon.ico"],
canonical = "https://docs.sciml.ai/Catalyst/stable/"),
modules = [Catalyst, ModelingToolkit],
modules = [Catalyst, ModelingToolkit,
isdefined(Base, :get_extension) ? Base.get_extension(Catalyst, :CatalystGraphMakieExtension) :
Catalyst.CatalystGraphMakieExtension],
doctest = false,
clean = true,
pages = pages,
Expand Down
2 changes: 1 addition & 1 deletion docs/pages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pages = Any[
"Steady state analysis" => Any[
"steady_state_functionality/homotopy_continuation.md",
"steady_state_functionality/nonlinear_solve.md",
"steady_state_functionality/steady_state_stability_computation.md",
#"steady_state_functionality/steady_state_stability_computation.md",
"steady_state_functionality/bifurcation_diagrams.md",
"steady_state_functionality/dynamical_systems.md"
],
Expand Down
11 changes: 4 additions & 7 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ isequivalent
==(rn1::ReactionSystem, rn2::ReactionSystem)
```

## Network visualization
## [Network visualization](@id network_visualization)
[Latexify](https://korsbo.github.io/Latexify.jl/stable/) can be used to convert
networks to LaTeX equations by
```julia
Expand All @@ -292,13 +292,10 @@ displayed as the ODE form)

Finally, another optional argument (`expand_functions=true`) automatically expands functions defined by Catalyst (such as `mm`). To disable this, set `expand_functions=false`.

If [Graphviz](https://graphviz.org/) is installed and commandline accessible, it
can be used to create and save network diagrams using [`Graph`](@ref) and
[`savegraph`](@ref).
Reaction networks can be plotted using the `GraphMakie` extension, which is loaded whenever all of `Catalyst`, `GraphMakie`, and `NetworkLayout` are loaded (note that a Makie backend, like `CairoMakie`, must be loaded as well). The two functions for plotting networks are `plot_network` and `plot_complexes`, which are two distinct representations.
```@docs
Graph
complexgraph
savegraph
plot_network(::ReactionSystem)
plot_complexes(::ReactionSystem)
```

## [Rate laws](@id api_rate_laws)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ etc).
- Models can be [coupled with events](@ref constraint_equations_events) that affect the system and its state during simulations.
- By leveraging ModelingToolkit, users have a variety of options for generating optimized system representations to use in solvers. These include construction of [dense or sparse Jacobians](@ref ode_simulation_performance_sparse_jacobian), [multithreading or parallelization of generated derivative functions](@ref ode_simulation_performance_parallelisation), [automatic classification of reactions into optimized jump types for Gillespie type simulations](https://docs.sciml.ai/JumpProcesses/stable/jump_types/#jump_types), [automatic construction of dependency graphs for jump systems](https://docs.sciml.ai/JumpProcesses/stable/jump_types/#Jump-Aggregators-Requiring-Dependency-Graphs), and more.
- [Symbolics.jl](https://github.com/JuliaSymbolics/Symbolics.jl) symbolic expressions and Julia `Expr`s can be obtained for all rate laws and functions determining the deterministic and stochastic terms within resulting ODE, SDE, or jump models.
- [Steady states](@ref homotopy_continuation) (and their [stabilities](@ref steady_state_stability)) can be computed for model ODE representations.
- [Steady states](@ref homotopy_continuation) can be computed for model ODE representations.

#### [Features of Catalyst composing with other packages](@id doc_index_features_composed)
- [OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl) Can be used to numerically solve generated reaction rate equation ODE models.
- [StochasticDiffEq.jl](https://github.com/SciML/StochasticDiffEq.jl) can be used to numerically solve generated Chemical Langevin Equation SDE models.
- [JumpProcesses.jl](https://github.com/SciML/JumpProcesses.jl) can be used to numerically sample generated Stochastic Chemical Kinetics Jump Process models.
- Support for [parallelization of all simulations](@ref ode_simulation_performance_parallelisation), including parallelization of [ODE simulations on GPUs](@ref ode_simulation_performance_parallelisation_GPU) using [DiffEqGPU.jl](https://github.com/SciML/DiffEqGPU.jl).
- [Latexify](https://korsbo.github.io/Latexify.jl/stable/) can be used to [generate LaTeX expressions](@ref visualisation_latex) corresponding to generated mathematical models or the underlying set of reactions.
- [Graphviz](https://graphviz.org/) can be used to generate and [visualize reaction network graphs](@ref visualisation_graphs) (reusing the Graphviz interface created in [Catlab.jl](https://algebraicjulia.github.io/Catlab.jl/stable/)).
- [GraphMakie](https://docs.makie.org/stable/) recipes are provided that can be used to generate and [visualize reaction network graphs](@ref visualisation_graphs)
- Model steady states can be [computed through homotopy continuation](@ref homotopy_continuation) using [HomotopyContinuation.jl](https://github.com/JuliaHomotopyContinuation/HomotopyContinuation.jl) (which can find *all* steady states of systems with multiple ones), by [forward ODE simulations](@ref steady_state_solving_simulation) using [SteadyStateDiffEq.jl)](https://github.com/SciML/SteadyStateDiffEq.jl), or by [numerically solving steady-state nonlinear equations](@ref steady_state_solving_nonlinear) using [NonlinearSolve.jl](https://github.com/SciML/NonlinearSolve.jl).
[BifurcationKit.jl](https://github.com/bifurcationkit/BifurcationKit.jl) can be used to compute bifurcation diagrams of model steady states (including finding periodic orbits).
- [DynamicalSystems.jl](https://github.com/JuliaDynamics/DynamicalSystems.jl) can be used to compute model [basins of attraction](@ref dynamical_systems_basins_of_attraction), [Lyapunov spectrums](@ref dynamical_systems_lyapunov_exponents), and other dynamical system properties.
Expand Down
26 changes: 15 additions & 11 deletions docs/src/introduction_to_catalyst/introduction_to_catalyst.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,18 @@ latexify(rn)
```@example tut1
rn #hide
```
Assuming [Graphviz](https://graphviz.org/) is installed and command line
accessible, within a Jupyter notebook we can also graph the reaction network by
```julia
g = Graph(rn)
Catalyst also has functionality for visualizing networks using the [Makie](https://docs.makie.org/stable/)
plotting ecosystem. The relevant packages to load are Catalyst, GraphMakie, NetworkLayout, and a Makie backend
such as CairoMakie. Doing so and then using the `plot_network` function allows us to
visualize the network:
```@example tut1
using Catalyst
import CairoMakie, GraphMakie, NetworkLayout
g = plot_network(rn)
```
giving

![Repressilator solution](../assets/repressilator.svg)

The network graph shows a variety of information, representing each species as a
blue node, and each reaction as an orange dot. Black arrows from species to
blue node, and each reaction as an green node. Black arrows from species to
reactions indicate reactants, and are labelled with their input stoichiometry.
Similarly, black arrows from reactions to species indicate products, and are
labelled with their output stoichiometry. In contrast, red arrows from a species
Expand All @@ -105,8 +106,11 @@ hillr(P₂,α,K,n), ∅ --> m₃
have rates that depend on the proteins, and hence lead to red arrows from each
`Pᵢ`.

Note, from the REPL or scripts one can always use [`savegraph`](@ref) to save
the graph (assuming `Graphviz` is installed).
Note, from the REPL or scripts one can always use Makie's `save` function to save
the graph.
```julia
save("repressilator_graph.png", g)
```

## Mass action ODE models
Let's now use our `ReactionSystem` to generate and solve a corresponding mass
Expand Down Expand Up @@ -176,7 +180,7 @@ At this point we are all set to solve the ODEs. We can now use any ODE solver
from within the
[OrdinaryDiffEq.jl](https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)
package. We'll use the recommended default explicit solver, `Tsit5()`, and then
plot the solutions:
plot the solutions:

```@example tut1
sol = solve(oprob, Tsit5(), saveat=10.0)
Expand Down
96 changes: 79 additions & 17 deletions docs/src/model_creation/model_visualisation.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,43 +36,105 @@ If you wish to copy the output to your [clipboard](https://en.wikipedia.org/wiki
For a model to be nicely displayed you have to use an IDE that actually supports this (such as a [notebook](https://jupyter.org/)). Other environments (such as [the Julia REPL](https://docs.julialang.org/en/v1/stdlib/REPL/)) will simply return the full LaTeX code which would generate the desired expression.

## [Displaying model networks](@id visualisation_graphs)
A network graph showing a Catalyst model's species and reactions can be displayed using the `Graph` function. This first requires [Graphviz](https://graphviz.org/) to be installed and command line accessible. Here, we first declare a [Brusselator model](@ref basic_CRN_library_brusselator) and then displays its network topology:
Catalyst uses `GraphMakie` to display representations of chemical reaction networks, including the complex graph and the species-reaction graph (which is similar to the [Petri net](https://en.wikipedia.org/wiki/Petri_net) representation). To get started, import Catalyst, GraphMakie, and NetworkLayout to load the `CatalystGraphMakieExtension` extension, and then load a Makie backend (`CairoMakie` is a good lightweight choice).

```@example visualisation_graphs
using Catalyst, GraphMakie, NetworkLayout
using CairoMakie
nothing # hide
```

Let's declare a [Brusselator model](@ref basic_CRN_library_brusselator) to see this plotting functionality. The functions `plot_network` and `plot_complexes` are used to create the species-reaction and complex graphs, respectively. For a more thorough description of these two representations, please see the [network visualization](@ref network_visualization) section of the API, but the gist is that the species-reaction graph has species and reactions as nodes, and the complex graph has reaction complexes as nodes. Below we will plot the species-reaction graph using `plot_network`.
```@example visualisation_graphs
using Catalyst
brusselator = @reaction_network begin
A, ∅ --> X
1, 2X + Y --> 3X
B, X --> Y
1, X --> ∅
end
Graph(brusselator)
nothing # hide
plot_network(brusselator)
```
!["Brusselator Graph"](../assets/network_graphs/brusselator_graph.png)

The network graph represents species as blue nodes and reactions as orange dots. Black arrows from species to reactions indicate substrates, and are labelled with their respective stoichiometries. Similarly, black arrows from reactions to species indicate products (also labelled with their respective stoichiometries). If there are any reactions where a species affect the rate, but does not participate as a reactant, this is displayed with a dashed red arrow. This can be seen in the following [Repressilator model](@ref basic_CRN_library_repressilator):
The species-reaction graph (or network graph) represents species as blue nodes and reactions as green dots. Black arrows from species to reactions indicate substrates, and are labelled with their respective stoichiometries. Similarly, black arrows from reactions to species indicate products (also labelled with their respective stoichiometries). If there are any reactions where a species affect the rate, but does not participate as a reactant, this is displayed with a dashed red arrow. This can be seen in the following [Repressilator model](@ref basic_CRN_library_repressilator):
```@example visualisation_graphs
repressilator = @reaction_network begin
hillr(Z,v,K,n), ∅ --> X
hillr(X,v,K,n), ∅ --> Y
hillr(Y,v,K,n), ∅ --> Z
d, (X, Y, Z) --> ∅
end
Graph(repressilator)
nothing # hide
plot_network(repressilator)
```
!["Repressilator Graph"](../assets/network_graphs/repressilator_graph.png)

A generated graph can be saved using the `savegraph` function:
A generated graph can be saved using Makie's `save` function.
```julia
repressilator_graph = Graph(repressilator)
savegraph(repressilator_graph, "repressilator_graph.png")
repressilator_graph = plot_network(repressilator)
save("repressilator_graph.png", repressilator_graph)
```

Finally, a [network's reaction complexes](@ref network_analysis_reaction_complexes) (and the reactions in between these) can be displayed using the `complexgraph(brusselator)` function:
Finally, a [network's reaction complexes](@ref network_analysis_reaction_complexes) (and the reactions in between these) can be displayed using the `plot_complexes(brusselator)` function:
```@example visualisation_graphs
complexgraph(brusselator)
nothing # hide
plot_complexes(brusselator)
```
Here, reaction complexes are displayed as blue nodes, and reactions between complexes are displayed as black arrows. Red arrows indicate that the rate constantof a reaction has a species-dependence. Edges can be optionally labeled with their rate expressions by calling with the option `show_rate_labels`.
```@example visualisation_graphs
plot_complexes(brusselator, show_rate_labels = true)
```

## Customizing Plots
In this section we demonstrate some of the ways that plot objects can be manipulated to give nicer images. Let's start with our brusselator plot once again. Note that the `plot` function returns three objects: the `Figure`, the `Axis`, and the `Plot`, which can each be customized independently. See the general [Makie documentation](https://docs.makie.org/stable/) for more information.

```@example visualisation_graphs
f, ax, p = plot_complexes(brusselator, show_rate_labels = true)
```

It seems like a bit of the top node is cut off. Let's hide the tick marks and grid and increase the top and bottom margins by increasing `yautolimitmargin`.
```@example visualisation_graphs
hidedecorations!(ax)
ax.yautolimitmargin = (0.1, 0.1) # defaults to (0.05, 0.05)
ax.aspect = DataAspect()
```

There are many keyword arguments that can be passed to `plot_network` or `plot_complexes` to change the look of the graph (which get passed to the `graphplot` Makie recipe). Let's change the color of the nodes and make the inner labels a bit smaller. As before, we hide the tick marks and grid. Let's also give the plot a title.
```@example visualisation_graphs
f, ax, p = plot_complexes(brusselator, show_rate_labels = true, node_color = :yellow, ilabels_fontsize = 10)
hidedecorations!(ax)
ax.yautolimitmargin = (0.1, 0.1) # defaults to (0.05, 0.05)
ax.aspect = DataAspect()
ax.title = "Brusselator"
```

Most of the kwargs that modify the nodes or edges will also accept a vector with the same length as the number of nodes or edges, respectively. See [here](https://graph.makie.org/stable/#The-graphplot-Recipe) for a full list of keyword arguments to `graph_plot`. Note that `plot_complexes` and `plot_network` default to `layout = Stress()` rather than `layout = Spring()`, since `Stress()` is better at generating plots with fewer edge crossings. More layout options and customizations (such as pinning nodes to certain positions) can be found in the [`NetworkLayout` documentation](https://juliagraphs.org/NetworkLayout.jl/stable/).

Once a graph is already created we can also change the keyword arguments by modifying the fields of the `Plot` object `p`.
```@example visualisation_graphs
p.node_color = :orange
```

Custom node positions can also be given, if the automatic layout is unsatisfactory.
```@example visualisation_graphs
fixedlayout = [(0,0), (1,0), (0,1), (1,1), (2,0)]
p.layout = fixedlayout
autolimits!(ax)
```

Makie graph plots can be made to be interactive, allowing one to drag nodes and edges. To do this, we retrieve the axis from the GraphMakie plot, and then register the interactions. **Note that this can only be done if `GLMakie` is the installed Makie backend. See the [GraphMakie docs](https://graph.makie.org/stable/#Predefined-Interactions) for more information about the types of interactions one can register.** Below is a non-interactive code example that shows how to do this:

```julia
using GLMakie
f, ax, p = plot_network(brusselator)
deregister_interaction!(ax, :rectanglezoom)
register_interaction!(ax, :ndrag, NodeDrag(p))
register_interaction!(ax, :edrag, EdgeDrag(p))
```

The equivalent of `show` for Makie plots is the `display` function.
```julia
f = plot_network(brusselator)
display(f)
```

Once you are happy with the graph plot, you can save it using the `save` function.
```julia
save("fig.png", f)
```
!["Repressilator Complex Graph"](../assets/network_graphs/repressilator_complex_graph.png)
Here, reaction complexes are displayed as blue nodes, and reactions in between these as black arrows.
Loading

0 comments on commit 494ee45

Please sign in to comment.