From db43f45c2b4c4da9b72fac77c6921c62c0fb0b77 Mon Sep 17 00:00:00 2001 From: Eng Zer Jun Date: Wed, 8 Nov 2023 11:42:31 +0800 Subject: [PATCH] graph/path: do not keep duplicate paths in YenKShortestPaths Previously the code did not ignore spur paths that had already been added into the list of potential paths. This could cause the search to return duplicate paths for certain graphs. --- graph/path/yen_ksp.go | 26 +++++++++++++++++++++++++- graph/path/yen_ksp_test.go | 22 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/graph/path/yen_ksp.go b/graph/path/yen_ksp.go index 04f6e50c..82b1b808 100644 --- a/graph/path/yen_ksp.go +++ b/graph/path/yen_ksp.go @@ -85,7 +85,18 @@ func YenKShortestPaths(g graph.Graph, k int, cost float64, s, t graph.Node) [][] spath = append(root[:len(root)-1], spath...) weight += rootWeight } - pot = append(pot, yenShortest{spath, weight}) + + // Add the potential k-shortest path if it is new. + isNewPot := true + for x := range pot { + if isSamePath(pot[x].path, spath) { + isNewPot = false + break + } + } + if isNewPot { + pot = append(pot, yenShortest{spath, weight}) + } } if len(pot) == 0 { @@ -104,6 +115,19 @@ func YenKShortestPaths(g graph.Graph, k int, cost float64, s, t graph.Node) [][] return paths } +func isSamePath(a, b []graph.Node) bool { + if len(a) != len(b) { + return false + } + + for i, x := range a { + if x.ID() != b[i].ID() { + return false + } + } + return true +} + // yenShortest holds a path and its weight for sorting. type yenShortest struct { path []graph.Node diff --git a/graph/path/yen_ksp_test.go b/graph/path/yen_ksp_test.go index eabf4a91..70702c66 100644 --- a/graph/path/yen_ksp_test.go +++ b/graph/path/yen_ksp_test.go @@ -306,6 +306,28 @@ var yenShortestPathTests = []struct { {8, 2, 4, 3, 7, 5}, }, }, + { + name: "grid", // This is the failing case in gonum/gonum#1920. + graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedUndirectedGraph(0, math.Inf(1)) }, + edges: []simple.WeightedEdge{ + {F: simple.Node(1), T: simple.Node(2), W: 1}, + {F: simple.Node(2), T: simple.Node(3), W: 2}, + {F: simple.Node(3), T: simple.Node(6), W: 5}, + {F: simple.Node(1), T: simple.Node(4), W: 5}, + {F: simple.Node(2), T: simple.Node(5), W: 4}, + {F: simple.Node(4), T: simple.Node(5), W: 6}, + {F: simple.Node(5), T: simple.Node(6), W: 7}, + }, + query: simple.Edge{F: simple.Node(1), T: simple.Node(6)}, + k: -1, + cost: math.Inf(1), + wantPaths: [][]int64{ + {1, 2, 3, 6}, + {1, 2, 5, 6}, + {1, 4, 5, 6}, + {1, 4, 5, 2, 3, 6}, + }, + }, } func bipartite(n int, weight, inc float64) []simple.WeightedEdge {