I’ve been lurking over at Reddit’s /r/dailyprogrammer for some time now, watching for challenges that would be suited to an FPGA. This past week I found one that was perfect:

In graph theory, a graph’s radius is the minimum eccentricity of any vertex for a given graph. More simply: it is the minimum distance between all possible pairs of vertices in a graph. As an example, the Petersen graph has a radius of 2 because any vertex is connected to any other vertex within 2 edges. On the other hand, the Butterfly graph has a radius of 1 since its middle vertex can connect to any other vertex within 1 edge, which is the smallest eccentricity of all vertices in this set. Any other vertex has an eccentricity of 2. Given an Adjacency matrix find the radius of the graph. The graph is not directed, so the matrix will always be reflected about the main diagonal. Print the radius of the graph as an integer.

My solution is written in Verilog and targets an Altera Cyclone IV FPGA development board. The final answer is shown on the board’s LCD display. The algorithm I describe here takes advantage of an FPGA’s parallelism and results in a O(n) solution.

The design instantiates a network of nodes that represent vertices in the graph. The nodes themselves determine the radius of the graph through a constant process of telling their neighbors to whom they’re connected and reporting their distance to every other node in the graph. Due to the distributed nature of the design the solution is found very quickly. For a graph with N nodes the solution is found in N clock cycles (or less).

The original post gave this example adjacency matrix to solve:

0 1 0 0 1 1 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 1 0 1 1 0 0





This matrix describes a graph of 10 vertices. Each row corresponds to a node (or vertex) in the graph. Each column of a given row tells us who that node’s neighbors are. For example, the first row corresponds to node 0 and tells us that its neighboring nodes are 1, 4 and 5.

If you followed the Wikipedia links in the problem statement above and started doodling you’ll discover that this adjacency matrix is describing a Petersen graph.

Very pretty, Petersen.

Before we tackle the Petersen graph lets work through a smaller example first to get a feel for the algorithm. Below is a graph with 5 vertices shown both as an adjacency matrix and, well, as a graph.

The basic idea is that each node tells its neighbors how far away it thinks it is from other nodes in the network. I call this the node’s distance vector. Each node then takes their neighbors’ distance vectors and constructs a distance matrix. The distance matrix describes how many hops it would take to get to any other node in the network through its neighbors. As the distance matrix is updated, each node find the shortest path to every other node in the graph and updates its distance vector. Through this process the nodes learn their distances to every other node.

At start up, the adjacency matrix is loaded into the network. Each node stores its own adjacency vector to keep track of who its immediate neighbors are. Using its adjacency vector each node starts to construct its distance matrix :

The distance from a node to itself is 0. The distance to an adjacent node is 1. If a node isn’t aware of a path from itself to another node then the distance is infinite. This is shown as a grey box in the pictures. I use -1 in the actual design (all 1’s binary).

Based on the distance matrix each node then broadcasts a distance vector which is the shortest distance from itself to each other node in the matrix.

Each node uses the distance vectors from its immediate neighbors to update its own distance matrix. This is done by adding 1 to each distance reported in the vector. The idea is that if my neighbor says some other node is x hops away and I’m 1 hop away from her then I must be x+1 hops away from that other node.

Here is what the distance matrix for each node looks like in the 2nd clock cycle:

Take a look at the second row of Node 1’s Distance Matrix (colored in bright green). That row is Node 1’s row. All of Node 1’s neighbors fill in their Node 1 row with this information but add 1 to the distances (colored in dark green). Node’s that aren’t Node 1’s neighbors keep that row blank (e.g. Node 4).

Now that we have some more information in the distance matrices, the distance vectors become more informative:

Each column in a node’s distance vector describes the shortest distance to every other node. This was found by comparing the values in each column of the distance matrix and choosing the smallest one.

In the 3rd and final clock cycle the distance matrices look like this:

Here are the final distance vectors:

We know we are done because every entry in every distance vector has a value (there are no blanks). So, to find the radius of this graph we just find the longest reported distance: 3.

Part of the challenge of implementing this algorithm in an FPGA was figuring out how to store and access the distance vectors and matrices. We’d like each node to be able to take a 2D array as input but Verilog won’t let us do that. We’ve put people on the moon, my phone is a hand held super computer by 1984’s standards (when Verilog was created) and Verilog still can’t do 2D ports.

Here’s what I finally came up with:

The distance vectors are packed together as a 1D input and then unpacked into a 2D array inside of the module. The remote matrix (dist_matrix_rmt) is what’s advertised by neighboring nodes. The local matrix (dist_matrix_lcl) is the data structure local to this node.

Below is the portion of Node that actually updates the local distance matrix and is the heart of the algorithm. Keep in mind that there is no actual “looping” going on here - it’s all happening in parallel!

The information in the local distance matrix is then used to update the node’s output distance vector. This process below just “loops” through the rows of every column finding the smallest value. Again, these nested for-loops just describe a digital circuit that determine every value of the vector in parallel. It all happens in a single clock cycle.

Finally, all of these nodes are instantiated with a generate loop. Notice that each node’s distance vector is just packed into the signal that becomes the distance matrix inputs to all of the other nodes.

Here’s what the board looks like showing the final answer to the Petersen graph: