Search code examples
optimizationgraphlinear-programminglpsolvelongest-path

Graph longest path using linear programming


I have a weighted directed graph where there are no cycles, and I wish to define the constraints so that I can solve a maximization of the weights of a path with linear programming. However, I can't wrap my head around how to do that.

For this I wish to use the LPSolve tool. I thought about making an adjacency matrix, but I don't know how I could make that work with LPSolve.

How can I define the possible paths from each node using constraints and make it generic enough that it would be simple to adapt to other graphs?


Solution

  • Since you have a weighted directed graph, it is sufficient to define a binary variable x_e for each edge e and to add constraints specifying that the source node has flow balance 1 (there is one more outgoing edge selected than incoming edge), the destination node has flow balance -1 (there is one more incoming edge than outgoing edge selected), and every other node has flow balance 0 (there are the same number of outgoing and incoming edges selected). Since your graph has no cycles, this will result in a path from the source to the destination (assuming one exists). You can maximize the weights of the selected edges.

    I'll continue the exposition in R using the lpSolve package. Consider a graph with the following edges:

    (edges <- data.frame(source=c(1, 1, 2, 3), dest=c(2, 3, 4, 4), weight=c(2, 7, 3, -4)))
    #   source dest weight
    # 1      1    2      2
    # 2      1    3      7
    # 3      2    4      3
    # 4      3    4     -4
    

    The shortest path from 1 to 4 is 1 -> 2 -> 4, with weight 5 (1 -> 3 -> 4 has weight 3).

    We need the flow balance constraints for each of our four nodes:

    source <- 1
    dest <- 4
    (nodes <- unique(c(edges$source, edges$dest)))
    # [1] 1 2 3 4
    (constr <- t(sapply(nodes, function(n) (edges$source == n) - (edges$dest == n))))
    #      [,1] [,2] [,3] [,4]
    # [1,]    1    1    0    0
    # [2,]   -1    0    1    0
    # [3,]    0   -1    0    1
    # [4,]    0    0   -1   -1
    (rhs <- ifelse(nodes == source, 1, ifelse(nodes == dest, -1, 0)))
    # [1]  1  0  0 -1
    

    Now we can put everything together into our model and solve:

    library(lpSolve)
    mod <- lp(direction = "max", 
              objective.in = edges$weight,
              const.mat = constr,
              const.dir = rep("=", length(nodes)),
              const.rhs = rhs,
              all.bin = TRUE)
    edges[mod$solution > 0.999,]
    #   source dest weight
    # 1      1    2      2
    # 3      2    4      3
    mod$objval
    # [1] 5