Search code examples
algorithmgraphpath-findingadjacency-matrix

Path reconstruction - Pseudo-Multiply (Matrix multiplication) Algorithm


Context

I am currently working on path reconstruction in some of my graph algorithms. For the single-source-shortest-paths problem I used an array of predecessors to reconstruct the shortest path from one source node to all the other nodes.

Quick example: [0, 3, 0, 0]

The shortest path from source 0 to target 1 would be [0, 3, 1] because starting from the target node 1 the path can be constructed by going backwards using the 'parent' array. 1 has been reached over 3 and 3 has been reached over 0. 0 is the source. Done.


The next algorithms are all-pairs-shortest-paths algorithms. The easiest example has been the Floyd-Warshall algorithm which results in a matrix containing all 'successor'-nodes. A good example for the reconstruction pseudo code can be found on Wikipedia - Floyd Warshall. To summarize it: A matrix is used to store each successor from one specific source node. It basically follows the same approach as before just for each node as a source and going forward instead of backwards.

Question - How to create the matrix of successors in case of the pseudo multiply algorithm?

Let's have a look at the algorithm first:

    for(int m = 0; m < nodeCount - 1; m++) {
        Matrix nextResultMatrix = new Matrix(nodeCount, nodeCount, Integer.MAX_VALUE);

        for(int i = 0; i < nodeCount; i++) {
            for(int j = 0; j < nodeCount; j++) {
                int value = Integer.MAX_VALUE;

                for(int k = 0; k < nodeCount; k++) {
                    value = Math.min(
                                value,
                                resultMatrix.at(i, k) + sourceMatrix.at(k, j)
                            );
                }
                nextResultMatrix.setAt(i, j, value);
            }
        }
        resultMatrix = nextResultMatrix;
    }

In each iteration the matrix for the shortest paths of length m will be calculated. The inner loop is pretty similar to the matrix multiplication itself. In the most inner loop the algorithm checks wether the current path is shorter than the path from source i over k to target j. After the inner k-loop has finished the value inside of the new result matrix is set. Which leads to the problem:

In case of the Floyd-Warshall algorithm it was easier to identify wether the path has been shorter and which node is now the successor. In this case the value that has been calculated in the k-loop will be set anyway. Is it possible to determine the successor here?

Thoughts about a possible solution

  • The pseudo multiply algorithm provides a matrix for each iteration which represents the shortest paths of length m. Might this help to find a solution without increasing the - already quite bad - time complexity and without having to store each matrix at the same time.
  • I found an interesting idea in a comment here on stackoverflow which might lead to a solution reference. From what is stated there it seems to be quite heavy lifting for keeping track of the shortest paths. I haven't fully wrapped my head around the idea and how to implement this though.

Solution

  • My solution

    So after stepping through the algorithm and making clear what each step exactly means I was finally able to figure out a solution. I will try to explain the changes in the code here but first let me present the solution:

    for(int m = 0; m < nodeCount - 1; m++) {
        Matrix nextResultMatrix = new Matrix(nodeCount, nodeCount, Integer.MAX_VALUE);
    
        for(int i = 0; i < nodeCount; i++) {
            for(int j = 0; j < nodeCount; j++) {
                int value = resultMatrix.at(i, j);
                int shorterPathFoundOverNode = prevMatrix.at(i, j);
    
                // new shortest path from node i to j is
                // the minimum path that can be found from node i over k to j
                // k will be the node itself and every other node
    
                for(int k = 0; k < nodeCount; k++) {
    
                    if(resultMatrix.at(i, k) != Graph.NO_EDGE && sourceMatrix.at(k, j) != Graph.NO_EDGE) {
                        if(value > resultMatrix.at(i, k) + sourceMatrix.at(k, j)) {
    
                            // update value if path is shorter
                            value = resultMatrix.at(i, k) + sourceMatrix.at(k, j);
    
                            shorterPathFoundOverNode = k;
                        }
                    }
                }
    
                nextResultMatrix.setAt(i, j, value);
                prevMatrix.setAt(i, j, shorterPathFoundOverNode);
            }
        }
    
        resultMatrix = nextResultMatrix;
    }
    
    • A very basic but important idea was to replace the initialization value of value inside the j loop from Integer.MAX with the value that has previously been found or at first iteration the value that has been used to initialize the matrix (Integer.MAX). This was also important because the condition would have been true once per iteration which did not cause any problems before but now - since we perform more actions inside of the condition - it matters.

    • It was necessary to replace the Math.min method with an if condition to have the ability to do more than just setting the value.

    • To reconstruct the shortest paths the method of keeping track of the previous nodes is used. This is very similar to the single-source-shortest-paths problem as stated in the question. Of course it is required to use a matrix in this case because all nodes will be the source node.

    To summarize the idea: Setup an additional matrix that keeps track of the previous node for each target node. When iterating through the k loop save the previous node if a shorter path has been found (Important: Only if it is actually shorter than the previous path).