Skip to main content
Logo image

Section 3.9 Networks

PreFigure enables authors to create diagrams of networks, such as that shown in Figure 3.9.1. Mathematicians sometimes refer to these as graphs, but we will use the term network to distinguish them from graphs of functions. There is quite a bit of flexibility as will be explained in the remainder of this section.
Figure 3.9.1. A simple network.

Subsection 3.9.1 Getting started

Let’s begin by discussing Figure 3.9.1. Notice that it is a directed network and that there are multiple edges between two of the vertices. The PreFigure source is in Listing 3.9.2.
Listing 3.9.2. The PreFigure source for Figure 3.9.1.
<diagram dimensions="(300,300)" margins="5">
  <definition>graph={1:[3,4,5,5],2:[4,5],3:[4,5],6:[2,4]}</definition>
  <coordinates bbox="(-1,-1,1,1)">
    <network directed="yes" graph="graph" arrows="end"
             scale="0.8" node-fill="#fcf" node-stroke="black"
	     seed="1" labels="yes"
	     tactile-node-size="40">
    </network>
  </coordinates>
</diagram>
The network is defined by the dictionary in line 2 where each key in the dictionary defines a node in the network and the corresponding value lists the nodes connected by edges. Notice that node 1 is connected to node 5 by two edges, as seen in the diagram.
The <network> element then constructs the graphical representation of the network. There are quite a few attributes for that element so let’s take a moment to consider them.
graph
The @graph attribute tells PreFigure about the dictionary that defines the structure of the network.
directed
The attribute @directed="yes" declares this to be a directed network, which means that the edges have a direction indicated by arrows. If this attribute is set to "no", which is the default, then no arrows are placed on the edges.
arrows
For a directed graph, this attribute controls where the arrowheads appear. There are two values, @arrows="end" and @arrows="middle" with “middle” being the default.
labels
Use @labels="yes" to include labels inside the nodes.
scale
The nodes will fit just inside the bounding box defined by the current coordinate system so the value of @scale can be used to pull the nodes in toward the center. The default is @scale="0.8".
node-fill
The @node-fill attribute defines the color to use when filling the nodes. This is part of a collection of attributes that control the visual appearance of the network. Each <node> will generate a <point> so other attributes include @node-stroke, @node-thickness, @node-size, @node-style.
Similarly, an edge will generate a <path> so some path attributes can be applied, such as @edge-stroke, @edge-thickness, and @edge-dash.
loop-scale
As described in more detail below, this attribute provides some control over how the shape of loops.
label-dictionary
If the value of this attribute is a dictionary {0:'a', 1:'b'}, then the node whose handle is 0 will be given the label \(a\text{.}\) Similarly, the node whose handle is 1 will be given the label \(b\text{.}\) This is demonstrated in Figure 3.9.4.
seed
Some algorithms that determine the positions of the nodes uses a random seed. To ensure consistent behavior while you are developing a diagram, set the value of the @seed attribute so that the nodes will stay in the same position from one compilation to the next. If you are not satisfied with the appearance of the network, you could try using a different seed.

Subsection 3.9.2 A more verbose network

A second way of defining the same network is shown in Listing 3.9.3. In this case, we do not define the structure of the network using a dictionary. Instead, we include <node> elements as children of <network>. The @edges attribute for each node defines the edges connecting that node to others.
Listing 3.9.3. Another PreFigure source for Figure 3.9.1.
<diagram dimensions="(300,300)" margins="5">
  <coordinates bbox="(-1,-1,1,1)">
    <network directed="yes" scale="0.8"
	     node-fill="#fcf" node-stroke="black"
	     seed="1" labels="yes"
	     tactile-node-size="40">
      <node at="1" edges="[3,4,5,5]"/>
      <node at="2" edges="[4,5]"/>
      <node at="3" edges="[4,5]"/>
      <node at="4"/>
      <node at="5"/>
      <node at="6" edges="[2,4]"/>
    </network>
  </coordinates>
</diagram>
In fact, we can mix these two approaches to defining the network for more flexibility as demonstrated by the next example.
Figure 3.9.4. Using different features to define the network structure.
Listing 3.9.5. The PreFigure source for Figure 3.9.4.
<diagram dimensions="(250,250)" margins="5"
         xmlns="https://prefigure.org">
  <coordinates bbox="(-1,-1,1,1)">
    <definition>graph={0:[0,1], 1:[0]}</definition>
    <definition>labels={0:'a', 1:'b'}</definition>
    <network directed="yes" graph="graph" arrows="middle"
             seed="1" node-size="15" scale="0.5" node-fill="white"
             labels="yes" label-dictionary="labels">
      <node at="2" style="double-circle"><m>S</m></node>
      <edge vertices="(2,2)">1</edge>
      <edge vertices="(2,1)"/>
      <edge vertices="(2,0)"/>
      <edge vertices="(0,0)" label-location="0.6">1</edge>
    </network>
  </coordinates>
</diagram>
First notice that a subgraph is defined by the @graph attribute of the <network> element. Furthermore, a third node is defined using a <node>, which enables us to change some properties of this node, such as its style and its label. Some edges are added using <edge> elements, and one edge, which was added to the graph through the @graph attribute, is given a label and the location of the label specified by the @label-location attribute, which is a value between 0 and 1.
There are some general principles at play:
  • Nodes and edges can be defined through the network’s @graph attribute. Network features defined in this way inherit the properties, such as node style, defined as an attribute of the <network> element.
  • A <node> element should be defined with a handle given by its @at attribute. If the handle already exists in the graph as defined through the network’s @graph attribute, then this element will act as a replacement. If the handle is a new one, then this element is added to the graph structure. Edges defined through a node’s @edges attribute are considered to be new edges and so are added to the graph and drawn with the default appearance.
  • An <edge> element also obeys the previous principle. This provides a way to override the appearance of previously defined edges or to define new edges and their appearance.
A loop is just an edge that connects a node with itself. By default, a loop will be drawn approximately as a circle whose radius depends on the size of the node. As demonstrated in Figure 3.9.4, loops will be drawn centered in the largest gap between a node’s outgoing and incoming edges. However, the @loop-orientation attribute of a node can be used to change this behavior. The value of this attribute is an angle, given in degrees.
The dimensions of a loop can be controlled using the @loop-scale attribute of the <edge> element. The value of this attribute is an ordered pair of scaling factors, with the first scaling radially and the second tranversally, that are applied to the loop. This attribute may be added to the <network> in which case the value is applied to every loop that does not have a @loop-scale attribute in its <edge> element.
These last two attributes are demonstrated in Figure 3.9.6.
Figure 3.9.6. Some attributes to control the appearance of loops.
Listing 3.9.7. The PreFigure source for Figure 3.9.6.
<diagram dimensions="(300,150)"
         xmlns="https://prefigure.org">
  <coordinates bbox="(-1,-1,1,2)">
    <network directed="yes" arrows="end"
	     node-fill="lightskyblue" node-stroke="black"
             node-size="25" tactile-node-size="60"
	     labels="yes">
      <node at="0" p="(-0.5,0)" style="double-circle"
            loop-orientation="90">
        <m>S_0</m>
      </node>
      <node at="1" p="(0.5,0)" loop-orientation="90">
        <m>S_1</m>
      </node>
      <edge vertices="(1,0)">0</edge>
      <edge vertices="(0,1)">0</edge>
      <edge vertices="(0,0)" loop-scale="(0.6,0.5)">1</edge>
      <edge vertices="(1,1)" loop-scale="(0.6,0.5)">1</edge>
    </network>
  </coordinates>
</diagram>

Subsection 3.9.3 Positioning the nodes

PreFigure relies on the Python package networkx to determine the positions of the nodes, a process referred to as layout. This subsection shows some examples that demonstrate the possibilities.
First, we may explicitly declare where we would like the nodes to be by including a @p attribute for each node.
Figure 3.9.8. The complete graph on 5 vertices.
Listing 3.9.9. The PreFigure source for Figure 3.9.8.
<diagram dimensions="(300,300)" margins="5">
  <coordinates bbox="(-1,-1,1,1)">
    <definition>N=5</definition>
    <definition>f(t)=(cos(2*pi*t/N), sin(2*pi*t/N))</definition>
    <network scale="0.8" labels="yes"
	     node-fill="#ccf" node-stroke="black"
	     tactile-node-size="40">
      <node at="0" p="f(0)" edges="[1,2,3,4]"/>
      <node at="1" p="f(1)" edges="[2,3,4]"/>
      <node at="2" p="f(2)" edges="[3,4]"/>
      <node at="3" p="f(3)" edges="[4]"/>
      <node at="4" p="f(4)"/>
    </network>
  </coordinates>
</diagram>
Alternatively, we can define the structure of the graph by including a @graph attribute inside the <network> element and then using <node> elements to set the positions. This is seen in Listing 3.9.10.
Listing 3.9.10. A second source for Figure 3.9.8.
<diagram dimensions="(300,300)" margins="5">
  <coordinates bbox="(-1,-1,1,1)">
    <definition>graph={0:[1,2,3,4], 1:[2,3,4], 2:[3,4], 3:[4]}</definition>
    <definition>N=5</definition>
    <definition>f(t)=(cos(2*pi*t/N), sin(2*pi*t/N))</definition>
    <network scale="0.8" labels="yes" graph="graph"
	     node-fill="#ccf" node-stroke="black"
	     tactile-node-size="40">
      <node at="0" p="f(0)"/>
      <node at="1" p="f(1)"/>
      <node at="2" p="f(2)"/>
      <node at="3" p="f(3)"/>
      <node at="4" p="f(4)"/>
    </network>
  </coordinates>
</diagram>
Nodes are created when they are referenced in the dictionary defined by the @graph attribute. When a <node> element is encountered inside the <network> element, PreFigure checks to see if the node has already been defined. If not, it will add it to the graph using the @edges attribute to append more structure to the network. While this is allowed, it is not necessary and could be confusing to author and maintain a network defined like this.
In these last two examples, we have specified the position of the nodes as an attribute inside the <node> element. If we would like networkx to position the nodes, we have some more options.
We can prescribe a different layout method using the @layout attribute of the <network> element. The diagram in Figure 3.9.11 illustrates how the @layout="bfs" can be used to illustrate a binary tree.
Figure 3.9.11. A binary tree.
Listing 3.9.12. The PreFigure source for Figure 3.9.11.
<diagram dimensions="(300,300)" margins="5">
  <definition>
    graph={0:[1,2],1:[3,4],2:[5,6],3:[7,8],4:[9,10],5:[11,12],6:[13,14]}
  </definition>
  <coordinates bbox="(-1,-1,1,1)">
    <rectangle center="(0,0)" dimensions="(2,2)" stroke="black"/>
    <network layout="bfs" start="0" graph="graph" node-fill="orange"
	     rotate="-90" scale="0.8">
      <node at="0" fill="red"/>
      <edge vertices="[0,1]" stroke="blue" thickness="4"/>
    </network>
  </coordinates>
</diagram>
There are a couple of features to notice here. If we use the bfs algorithm for the layout, we need to specify a node at which to @start. This is an attribute of the <network> element.
The bfs algorithm produces a tree that moves from left to right. To depict the tree with the root at the top, we use @rotate="-90" to rotate the graph.
Since we would like the root to be colored differently, we use a <node> element to specify the color of that node.
In the same way, we use an <edge> element to modify the appearance of a particular edge that has already been added to the graph structure. If the edge has not been added previously, then it will be added to the structure of the graph.
Here are the possible values of the @layout attribute.
spring
This is the default if @layout is not specifically defined. This algorithm begins by randomly positioning the nodes, and an optional @seed attribute declares the seed for the algorithm.
bfs
This is the breadth-first search algorithm demonstrated above. The root of the tree needs to be declared with the @start attribute.
circular
The nodes are equally spaced around a circle.
random
Nodes are randomly positioned using the value of the @seed attribute, which is 1 by default.
bipartite
Used for bipartite graphs. The @bipartite-set attribute, whose value is a list of handles describing the nodes in one of the two sets, is required. The @alignment attribute can be set to “vertical” or “horizontal”, which is the default.
planar
Lays out planar graphs so that edges do not cross.
spectral
See the networkxdocumentation.
After the nodes are positioned by networkx, PreFigure sets up a coordinate transformation so that the nodes will be positioned in the current bounding box. Because a <network> may have loops or nodes drawn with a large size, an author may wish to pull the nodes into the center of the bounding box so that, say, a loop does not leave the bounding box. The @scale attribute of the <network>, which is 0.8 by default, determines the degree to which nodes are pulled into the center.

Subsection 3.9.4 Putting everything together

Networks have a lot of features so it may be worthwhile to show an example that illustrates how these features can be used in mathematical exposition. Suppose that we would like to explain the idea of a spanning tree as a subgraph of a graph \(G\text{.}\) On the left of Figure 3.9.13 is the graph \(G\) with a collection of dashed edges. If we remove those edges, we obtain a spanning tree as shown on the right. To make the point clear, we would like the nodes to be in the same positions in both diagrams.
Figure 3.9.13. A graph \(G\) with some edges indicated on the left. When those edges are removed, we have a spanning tree as seen on the right.
Listing 3.9.14. The PreFigure source for the left of Figure 3.9.13.
<diagram dimensions="(250,250)" margins="5">
  <definition>graph={1:[3,4,5],2:[4,5],3:[4,5],6:[2,4]}</definition>
  <coordinates bbox="(-1,-1,1,1)">
    <network graph="graph" scale="0.8"
	     node-fill="#fcf" node-stroke="black"
	     seed="1" labels="yes" node-style="box"
	     tactile-node-size="40">
      <edge vertices="[3,5]" dash="9 9"/>
      <edge vertices="[1,4]" dash="9 9"/>
      <edge vertices="[4,6]" dash="9 9"/>
      <edge vertices="[2,4]" dash="9 9"/>
    </network>
  </coordinates>
</diagram>
The PreFigure source for Figure 3.9.13 is shown in Listing 3.9.14. Notice how the structure of the graph is defined using a dictionary with instructions given to draw some edges as dashed lines.
The PreFigure source to create the diagram on the right is quite similar except that we replace @dash="9 9" in the <edge> elements with @stroke="none".
Listing 3.9.15. The PreFigure source for the right of Figure 3.9.13.
<diagram dimensions="(250,250)" margins="5">
  <definition>graph={1:[3,4,5],2:[4,5],3:[4,5],6:[2,4]}</definition>
  <coordinates bbox="(-1,-1,1,1)">
    <network graph="graph" scale="0.8"
	     node-fill="#fcf" node-stroke="black"
	     seed="1" labels="yes" node-style="box"
	     tactile-node-size="40">
      <edge vertices="[3,5]" stroke="none"/>
      <edge vertices="[1,4]" stroke="none"/>
      <edge vertices="[4,6]" stroke="none"/>
      <edge vertices="[2,4]" stroke="none"/>
    </network>
  </coordinates>
</diagram>

Subsection 3.9.5 Annotating networks

Handles are automatically given to the nodes and edges in a <network> so that the network can be annotated. For instance,
  • when a node is defined with the label “a”, its handle becomes “node-a”.
  • an edge from node “a” to node “b” has the handle “edge-a-b”.
  • a loop at node “a” has the handle “loop-a”.
  • if there are multiple edges from node “a” to node “b”, they acquire the handles “edge-a-b-0”, “edge-a-b-1”, and so forth. The same is true for loops.
Figure 3.9.16 demonstrates an annotated network.
Diagram Exploration Keyboard Controls
Key Action
Enter, A Activate keyboard driven exploration
B Activate menu driven exploration
Escape Leave exploration mode
Cursor down Explore next lower level
Cursor up Explore next upper level
Cursor right Explore next element on level
Cursor left Explore previous element on level
X Toggle expert mode
W Extra details if available
Space Repeat speech
M Activate step magnification
Comma Activate direct magnification
N Deactivate magnification
Z Toggle subtitles
C Cycle contrast settings
T Monochrome colours
L Toggle language (if available)
K Kill current sound
Y Stop sound output
O Start and stop sonification
P Repeat sonification output
Figure 3.9.16. An annotated network whose source is given in Listing 3.9.17.
Listing 3.9.17. The PreFigure source for Figure 3.9.16.
<diagram dimensions="(300,300)" margins="5"
         xmlns="https://prefigure.org"> 
  <definition>graph={1:[3,4,5,5],2:[4,5],3:[4,5],6:[2,4]}</definition>
  <network directed="yes" graph="graph" scale="0.7"
	   node-fill="#fcf" node-stroke="black"
           node-style="circle" node-size="15"
           arrows="end" seed="1" labels="yes"
	   tactile-node-size="45">
    <edge vertices="(1,2)">4</edge>
    <edge vertices="(2,2)" label-location="0.3"
          loop-scale="(0.8,0.8)">
      <m>\alpha</m>
    </edge>
    <node at="2" style="double-circle"/>
  </network>
  
  <annotations>
    <annotation ref="figure" text="A directed network with nodes and edges">
      <annotation ref="node-1" text="The node 1">
        <annotation ref="edge-1-3" text="An edge from node 1 to node 3"/>
        <annotation ref="edge-1-4" text="An edge from node 1 to node 4"/>
        <annotation ref="edge-1-5-0" text="An edge from node 1 to node 5"/>
        <annotation ref="edge-1-5-1" text="A second edge from node 1 to node 5"/>
      </annotation>
      <annotation ref="node-2" text="The node 2">
        <annotation ref="edge-2-4" text="An edge from node 2 to node 4"/>
        <annotation ref="edge-2-5" text="An edge from node 2 to node 5"/>
        <annotation ref="loop-2" text="A loop from node 2 to itself"/>
      </annotation>
      <annotation ref="node-3" text="The node 2">
        <annotation ref="edge-1-3" text="An incoming edge from node 1"/>
        <annotation ref="edge-3-4" text="An edge from node 3 to node 4"/>
        <annotation ref="edge-3-5" text="An edge from node 3 to node 5"/>
      </annotation>
      <annotation ref="node-5" text="The node 5 and four incoming edges">
        <annotation ref="edge-1-5-0" text="An incoming edge from node 1"/>
        <annotation ref="edge-1-5-1" text="A second incoming edge from node 1"/>
        <annotation ref="edge-2-5" text="An incoming edge from node 2"/>
        <annotation ref="edge-3-5" text="An incoming edge from node 3"/>
      </annotation>
      <annotation ref="node-6" text="The node 2">
        <annotation ref="edge-6-2" text="An edge from node 6 to node 2"/>
        <annotation ref="edge-6-4" text="An edge from node 6 to node 4"/>
      </annotation>
    </annotation>
  </annotations>
</diagram>