Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphMakie docs update #1130

Merged
merged 50 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9be8f2c
init
vyudu Nov 20, 2024
89f0bb4
add plots, etc
vyudu Nov 20, 2024
99a6e40
up
vyudu Nov 21, 2024
1b5774e
Merge remote-tracking branch 'origin/master' into graph-docs
vyudu Nov 21, 2024
d0b0c98
Auto stash before merge of "graph-docs" and "origin/master"
vyudu Nov 21, 2024
6320854
up
vyudu Nov 21, 2024
206a2f4
adding packages
vyudu Nov 21, 2024
db76ecc
up
vyudu Nov 21, 2024
2b79e7f
fix
vyudu Nov 21, 2024
b620390
remove @example@ blocks
vyudu Nov 21, 2024
fcc1b9a
revert
vyudu Nov 21, 2024
48198e7
up
vyudu Nov 21, 2024
7599962
fix
vyudu Nov 21, 2024
506b9dc
up
vyudu Nov 21, 2024
c879e14
up
vyudu Nov 22, 2024
d803b3b
up
vyudu Nov 22, 2024
9f2c47f
up
vyudu Nov 22, 2024
0aa8ae2
more fixes, making things dynamic
vyudu Nov 22, 2024
b924879
up
vyudu Nov 22, 2024
1ac195b
api fix
vyudu Nov 22, 2024
73254a9
rm graphs
vyudu Nov 22, 2024
c018934
rm graphs.jl include
vyudu Nov 22, 2024
d5308cd
fixes
vyudu Nov 23, 2024
735d3b5
Merge remote-tracking branch 'origin/master' into graph-docs
vyudu Nov 23, 2024
5ece6af
comment out stability tests
vyudu Nov 23, 2024
f3eb413
up
vyudu Nov 23, 2024
32e4c73
test fix
vyudu Nov 23, 2024
c2b8654
Merge branch 'master' into graph-docs
vyudu Nov 26, 2024
7dda201
Merge remote-tracking branch 'origin/master' into graph-docs
vyudu Nov 27, 2024
caf6a47
up
vyudu Nov 27, 2024
8d8ee6a
up
vyudu Nov 27, 2024
a9a7b1c
Update docs/src/index.md
vyudu Dec 13, 2024
f3e1989
up
vyudu Dec 13, 2024
2360170
Merge branch 'graph-docs' of github.com:vyudu/Catalyst.jl into graph-…
vyudu Dec 13, 2024
3277354
Merge branch 'master' of https://github.com/SciML/Catalyst.jl into gr…
vyudu Jan 6, 2025
1e88899
merge master, make rate labels optional, drop GLMakie
vyudu Jan 6, 2025
f903952
drop more GLMakie instances
vyudu Jan 6, 2025
3656db5
fix typo
vyudu Jan 6, 2025
415ae3b
add NetworkLayout to doc dependencies
vyudu Jan 6, 2025
d4e08c2
fixes
vyudu Jan 7, 2025
0667614
updating docs
vyudu Jan 8, 2025
706f3fe
test fixes
vyudu Jan 8, 2025
5918d90
uncap SciMLBase
vyudu Jan 8, 2025
4f20b5d
Merge branch 'master' of https://github.com/SciML/Catalyst.jl into gr…
vyudu Jan 9, 2025
a71f454
try docstring fix
vyudu Jan 9, 2025
145c7a6
fix reference
vyudu Jan 9, 2025
fbdc21d
remove assets/Project.toml
vyudu Jan 9, 2025
2d4c263
Merge branch 'master' of https://github.com/SciML/Catalyst.jl into gr…
vyudu Jan 9, 2025
11805d1
comment out stability
vyudu Jan 9, 2025
05fbd4f
up
vyudu Jan 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
```
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
Loading