1. Overview
In this tutorial, we’ll discuss how to draw a graph using LaTeX.
We’ll first start by listing the main LaTeX packages that we can use for graphs, and express their particular advantages.
Then, we’ll study some examples of graphs drawn with those packages, and also their variations.
At the end of this tutorial, we’ll be able to implement a graph in a LaTeX document.
2. A Short Recap on Graphs
2.1. Components of Graphs and Their Representation
A graph comprises a set of vertices and a set of edges. The edges can be either directed or undirected, and normally connect two vertices, not necessarily distinct. For hypergraphs, edges can also connect more than two edges, but we won’t treat them here.
The standard representation of a vertex consists of the image of a circle:
If the vertex has a label, this is typed inside the circle, as is the case with the number 1 in the example above. We can also represent multiple vertices by placing them in a non-overlapping manner anywhere in the image:
The standard representation of an edge corresponds to a line for undirected edges or an arrow for directed edges:
2.2. Typical Representation of Graphs
An image that represents a graph, therefore, consists of a set of circles on an empty field and a set of lines or arrows connecting them. Further, if the graph is a weighted graph, we can indicate the weights as labels on the edges:
And lastly, if the graph has loops, we can represent them as edges that connect a vertex to itself:
These are all the elements we need to represent a graph of any complexity. In the next section, we’ll first study the relevant packages for drawing graphs, and then see some examples of the implementation of graphs in code.
2.3. Alternative Representations of Graphs
We should also state, though, that graphical representations aren’t the only way to describe graphs intuitively. Other representations, and in particular edge lists, adjacency lists, or adjacency matrices, are also prevalent in practice.
In some cases, they may be more informative to a human reader than a drawn image of a graph. For example, a directed graph with 26 vertices and only two edges , is very simple to represent with formal notation:
Its full graphical representation would however occupy a significantly vast space and be mostly uninformative. This is because, if the graph we’re using possesses more than a handful of nodes or edges, it then becomes challenging to understand its structure by looking at its image. Some extensive graphs, such as the graph representing internet nodes, possess graphical representations that, while very beautiful, are however meaningless to a human.
For this reason, if our graph holds more than a few vertices or edges, we should avoid drawing it. Instead, we should use other types of formal representations such as adjacency matrices, edge lists, or adjacency lists:
3. Tikz
We can now discuss the packages that we can use to draw graphs in LaTeX. The most common LaTeX package used for drawing, in general, is TikZ, which is a layer over PGF that simplifies its syntax. TikZ is a powerful package that comes with several libraries dedicated to specific tasks, such as:
- Drawing mindmaps, useful for depicting conceptual relationships
- Diagrams for entity-relationships, which can assist us in representing knowledge graphs
- Drawing logical circuits, that are useful to represent operations in Boolean algebra
And also, relevant for our purposes, it contains a graphdrawing library that we can use for the automatic drawing of graphs. TikZ is the standard package we use to draw graphs, and we’ll dedicate it to most of the coded examples.
3.1. Basic Graph and Nodes
Drawings in TikZ are included in the tikzpicture environment. The simplest graph we can draw comprises of one vertex with its label, represented as a circle containing, for example, a variable:
\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[main/.style = {draw, circle}]
\node[main] (1) {$x_1$};
\end{tikzpicture}
\end{document}
The word main here refers to a style that we define when introducing the tikzpicture environment. In this case, we use it to tell the compiler to draw the shape of a circle for the nodes.
The most important command in the code above is \node. Its syntax is this:
\node[parameters] (nodeID) {nodeLabel};
We can use this command repeatedly to add more vertices to the graph:
\node[main] (2) [above right of=1] {$x_2$};
\node[main] (3) [below right of=1] {$x_3$};
\node[main] (4) [above right of=3] {$x_4$};
\node[main] (5) [above right of=4] {$x_5$};
\node[main] (6) [below right of=4] {$x_6$};
Notice how the term between the second pair of square brackets lets us define the position of the nodes in relation to other nodes. We can do this by using the keywords right, left, above, below, followed by a blank space, and then the keyword of and the label of the node to which they refer. This is particularly useful if we later need to rearrange the graph on the picture. In fact, by working with relative positions as opposed to absolute coordinates, we can move one anchor node around to move all others.
3.2. Adding Edges
We can also add edges by using the \draw command:
\draw[parameters] (fromWhere) -- (toWhere);
If we, for example, want to draw a line corresponding to an undirected edge between nodes (2) and (4), we can write:
\draw (2) -- (4);
If instead, we want to add an arrow for a directed edge, we can give the parameter -> to the \draw command:
\draw[->] (1) -- (2);
3.3. Distances and Widths
We can also notice that the lines appear to be too thin and that it’s difficult to distinguish between directed and undirected edges. We can make the graph more readable by passing the parameter thick to the tikzpicture environment, upon its introduction:
\begin{tikzpicture}[thick, main/.style = {draw, circle}]
...
\end{tikzpicture}
If the vertices appear too close to one another, we can also specify the parameter node distance to the tikzpicture environment to spread them further:
\begin{tikzpicture}[node distance={15mm}, thick, main/.style = {draw, circle}]
...
\end{tikzpicture}
3.4. More Challenging Edges
We may also want to connect with an edge some nodes for which the straight path intersects other edges or nodes. Say we try to connect with a straight path the nodes (1) and (5):
It doesn’t look good. Instead, we want to loop around the graph, from the outside and above node 2, and then descend back towards the graph. Luckily, we can do that by specifying a parameter looseness after declaring the initial point of a line. To better clarify where we want the edge to go, we also specify the angles out and in which indicate, respectively, the angle of the outgoing and ingoing edges:
\draw (1) to [out=135,in=90,looseness=1.5] (5);
3.5. Loops
We can use the same technique to draw loops in the graph, by indicating twice the same node as the starting and ending points of a loose line:
\draw (1) to [out=180,in=270,looseness=5] (1);
3.6. Draw Weighted Edges
If our graph is a weighted graph, we can add weighted edges as phantom nodes inside the \draw command:
\draw[->] (6) -- node[midway, above right, sloped, pos=1] {+1} (4);
With this command, we’re adding an invisible node halfway between vertices (6) and (4), and then to the top right of the edge’s middle point. The label {+1} indicates the weight of the graph. The parameters sloped and pos indicate, respectively, that we want the weight to be orthogonal to the edge, and shifted by a certain amount along the edge. We can determine by trial and error what is the correct amount, according to the size of our image.
3.7. The Full Graph
And finally, we can repeat all the instructions above to populate the graph with all edges we need:
\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[node distance={15mm}, thick, main/.style = {draw, circle}]
\node[main] (1) {$x_1$};
\node[main] (2) [above right of=1] {$x_2$};
\node[main] (3) [below right of=1] {$x_3$};
\node[main] (4) [above right of=3] {$x_4$};
\node[main] (5) [above right of=4] {$x_5$};
\node[main] (6) [below right of=4] {$x_6$};
\draw[->] (1) -- (2);
\draw[->] (1) -- (3);
\draw (1) to [out=135,in=90,looseness=1.5] (5);
\draw (1) to [out=180,in=270,looseness=5] (1);
\draw (2) -- (4);
\draw (3) -- (4);
\draw (5) -- (4);
\draw[->] (5) to [out=315, in=315, looseness=2.5] (3);
\draw[->] (6) -- node[midway, above right, sloped, pos=1] {+1} (4);
\end{tikzpicture}
\end{document}
4. Neuralnetwork
Another useful package for generating graphs is Neuralnetwork. This package, as the name suggests, is incredibly helpful to draw the architecture of a neural network, and in particular of feedforward neural networks.
The package simplifies the construction of layers and their manipulation. It does so by shortening all commands for a single layer to one line.
Its main advantage is in the rapidity of describing a full graph with only a few lines of code. Instead, its main disadvantage lies in the scarce customizability of the individual characteristics of the graph.
4.1. Basics of the Package
In the LaTeX package Neuralnetwork, we define graphs inside an environment with the same name:
\begin{neuralnetwork}
...
\end{neuralnetwork}
Inside this environment, we can then add layers by using the \inputlayer, \hiddenlayer, and \outputlayer commands, according respectively to whether we refer to the first, hidden, or last layer of a neural network. The syntax is similar for all three commands:
\inputlayer[count=howManyNodes, bias=trueOrFalse, nodeclass={classOfNode}] {}
The parameter count indicates the number of nodes in the given layer. The term bias indicates instead whether the bias for that layer should be counted as a separate element. In that case, this bias wouldn’t be connected to the previous layer, when links are established. Lastly, the class of the node consists of either one of the predefined classes input node, hidden node, or output node, or also any of our custom classes.
4.2. Drawing a Neural Network
We can draw a simple neural network comprising of three features in the input, a hidden layer with two neurons and a bias term, and a single neuron in the output layer. This resembles closely the structure of a neural network for XOR classification:
\documentclass{article}
\usepackage{neuralnetwork}
\begin{document}
\begin{neuralnetwork}
\inputlayer[count=3, bias=false]{}
\hiddenlayer[count=2]{}
\outputlayer[count=1]{}
\end{neuralnetwork}
\end{document}
4.3. Adding Edges and Labels
We can also add edges between each layer, by following each layer that we want to connect with its preceding layer by the \linklayers command:
...
\inputlayer[count=3, bias=false]{} \linklayers
\hiddenlayer[count=2]{} \linklayers
\outputlayer[count=1]{}
...
Notice how the yellow node, indicating the bias term in the intermediate layer, isn’t connected to the input layer. We can also add labels to the nodes, though this is somewhat tricky. The way to do it is by defining a new LaTeX command, and passing it as a parameter text to a layer:
...
\newcommand{\x}[2]{$x_#2$}
\inputlayer[count=3, bias=false, text=\x]
...
We can also follow the same criterion with the hidden layer and the output layer by creating appropriate commands:
...
\newcommand{\h}[2]{$h_#2$}
\newcommand{\y}[2]{$y_#2$}
...
\hiddenlayer[count=2, text=\h]{} \linklayers
\outputlayer[count=1, text=\y]{} \linklayers
...
The bias term in the hidden layer automatically takes the index of , as is common in the literature, without having to specify any particular instructions.
4.4. Weights
We can also add weights to the edges by using a formula like the one below. We have to define a new command first, and then assign it to the default text for links:
\newcommand{\w}[4] {$w_{#2,#4}$}
\setdefaultlinklabel{\w}
Here, #1 indicates the layer of origin, #2 the node of origin, and #3 and #4 indicate layer and node of destination, respectively. Notice that, while the weights of the hidden layer look normal, some weights in the previous layer are however missing.
This is because they’re automatically rewritten by some other weights, and as a consequence, we only see some of them. The only way to address this problem is by defining all links individually and then giving them the appropriate label. We can do this by using the \link command, instead of \linklayers:
...
\inputlayer[count=3,bias=false, text=\x]{}
\hiddenlayer[count=2, text=\h]{}
\link[style={}, labelpos=near start, from layer=0, from node=1, to layer=1, to node=1]
...
The parameter labelpos is the one we use to shift towards the node of origin. In this case, the label is assumed by the edge. This, in turn, avoids the overlapping between the edges’ labels.
4.5. Finishing Touches
We can then repeat this operation with two nested \foreach loops, which will let us place all labels in the correct, readable positions:
\foreach \n in {1,...,3}{
\foreach \m in {1,2}{
\link[style={}, labelpos=near start, from layer=0, from node=\n, to layer=1, to node=\m]
}
}
The network is now complete. We can replicate it in a LaTeX editor with this full code:
\documentclass{article}
\usepackage{neuralnetwork}
\begin{document}
\begin{neuralnetwork}
\newcommand{\x}[2]{$x_#2$}
\newcommand{\y}[2]{$y_#2$}
\newcommand{\h}[2]{$h_#2$}
\newcommand{\w}[4] {$w_{#2,#4}$}
\setdefaultlinklabel{\w}
\inputlayer[count=3, bias=false, text=\x]
\hiddenlayer[count=2, text=\h]
\foreach \n in {1,...,3}{
\foreach \m in {1,2}{
\link[style={}, labelpos=near start, from layer=0, from node=\n, to layer=1, to node=\m]
}
}
\outputlayer[count=1, text=\y] \linklayers
\end{neuralnetwork}
\end{document}
5. Converting to LaTeX Through Extensions
There’s also another option to mention that relates not so much to LaTeX packages themselves but, instead, to the automatic conversion into LaTeX code of graphs created in other graphical editors. This is particularly convenient if we’re used to drawing the graphs in other programming languages, and we’re still learning how to draw graphs in LaTeX.
There are, in fact, libraries or extension for the automatic conversion into LaTeX code of the graphical output of another editor:
- In Python, we can convert graphs created with matplotlib into LaTeX, thanks to the library tikzplotlib
- In MatLab, we can use the scripts from matlab2tikz to perform a similar operation
- In Blender 2.4, we can use blend2tikz to export Blender curves into LaTeX code
6. Conclusion
In this article, we studied how to draw graphs using LaTeX.