gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/graph/path/yen_ksp.go (about) 1 // Copyright ©2018 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package path 6 7 import ( 8 "math" 9 "sort" 10 11 "gonum.org/v1/gonum/graph" 12 "gonum.org/v1/gonum/graph/iterator" 13 ) 14 15 // YenKShortestPaths returns the k-shortest loopless paths from s to t in g 16 // with path costs no greater than cost beyond the shortest path. 17 // If k is negative, only path cost will be used to limit the set of returned 18 // paths. YenKShortestPaths will panic if g contains a negative edge weight. 19 func YenKShortestPaths(g graph.Graph, k int, cost float64, s, t graph.Node) [][]graph.Node { 20 // See https://en.wikipedia.org/wiki/Yen's_algorithm and 21 // the paper at https://doi.org/10.1090%2Fqam%2F253822. 22 23 _, isDirected := g.(graph.Directed) 24 yk := yenKSPAdjuster{ 25 Graph: g, 26 isDirected: isDirected, 27 } 28 29 if wg, ok := g.(Weighted); ok { 30 yk.weight = wg.Weight 31 } else { 32 yk.weight = UniformCost(g) 33 } 34 35 shortest, weight := DijkstraFrom(s, yk).To(t.ID()) 36 cost += weight // Set cost to absolute cost limit. 37 switch len(shortest) { 38 case 0: 39 return nil 40 case 1: 41 return [][]graph.Node{shortest} 42 } 43 paths := [][]graph.Node{shortest} 44 45 var pot []yenShortest 46 var root []graph.Node 47 for i := int64(1); k < 0 || i < int64(k); i++ { 48 // The spur node ranges from the first node to the next 49 // to last node in the previous k-shortest path. 50 for n := 0; n < len(paths[i-1])-1; n++ { 51 yk.reset() 52 53 spur := paths[i-1][n] 54 root := append(root[:0], paths[i-1][:n+1]...) 55 56 for _, path := range paths { 57 if len(path) <= n { 58 continue 59 } 60 ok := true 61 for x := 0; x < len(root); x++ { 62 if path[x].ID() != root[x].ID() { 63 ok = false 64 break 65 } 66 } 67 if ok { 68 yk.removeEdge(path[n].ID(), path[n+1].ID()) 69 } 70 } 71 for _, u := range root[:len(root)-1] { 72 yk.removeNode(u.ID()) 73 } 74 75 spath, weight := DijkstraFrom(spur, yk).To(t.ID()) 76 if weight > cost || math.IsInf(weight, 1) { 77 continue 78 } 79 if len(root) > 1 { 80 var rootWeight float64 81 for x := 1; x < len(root); x++ { 82 w, _ := yk.weight(root[x-1].ID(), root[x].ID()) 83 rootWeight += w 84 } 85 spath = append(root[:len(root)-1], spath...) 86 weight += rootWeight 87 } 88 89 // Add the potential k-shortest path if it is new. 90 isNewPot := true 91 for x := range pot { 92 if isSamePath(pot[x].path, spath) { 93 isNewPot = false 94 break 95 } 96 } 97 if isNewPot { 98 pot = append(pot, yenShortest{spath, weight}) 99 } 100 } 101 102 if len(pot) == 0 { 103 break 104 } 105 106 sort.Sort(byPathWeight(pot)) 107 best := pot[0] 108 if len(best.path) <= 1 || best.weight > cost { 109 break 110 } 111 paths = append(paths, best.path) 112 pot = pot[1:] 113 } 114 115 return paths 116 } 117 118 func isSamePath(a, b []graph.Node) bool { 119 if len(a) != len(b) { 120 return false 121 } 122 123 for i, x := range a { 124 if x.ID() != b[i].ID() { 125 return false 126 } 127 } 128 return true 129 } 130 131 // yenShortest holds a path and its weight for sorting. 132 type yenShortest struct { 133 path []graph.Node 134 weight float64 135 } 136 137 type byPathWeight []yenShortest 138 139 func (s byPathWeight) Len() int { return len(s) } 140 func (s byPathWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 141 func (s byPathWeight) Less(i, j int) bool { return s[i].weight < s[j].weight } 142 143 // yenKSPAdjuster allows walked edges to be omitted from a graph 144 // without altering the embedded graph. 145 type yenKSPAdjuster struct { 146 graph.Graph 147 isDirected bool 148 149 // weight is the edge weight function 150 // used for shortest path calculation. 151 weight Weighting 152 153 // visitedNodes holds the nodes that have 154 // been removed by Yen's algorithm. 155 visitedNodes map[int64]struct{} 156 157 // visitedEdges holds the edges that have 158 // been removed by Yen's algorithm. 159 visitedEdges map[[2]int64]struct{} 160 } 161 162 func (g yenKSPAdjuster) From(id int64) graph.Nodes { 163 if _, blocked := g.visitedNodes[id]; blocked { 164 return graph.Empty 165 } 166 nodes := graph.NodesOf(g.Graph.From(id)) 167 for i := 0; i < len(nodes); { 168 if g.canWalk(id, nodes[i].ID()) { 169 i++ 170 continue 171 } 172 nodes[i] = nodes[len(nodes)-1] 173 nodes = nodes[:len(nodes)-1] 174 } 175 if len(nodes) == 0 { 176 return graph.Empty 177 } 178 return iterator.NewOrderedNodes(nodes) 179 } 180 181 func (g yenKSPAdjuster) canWalk(u, v int64) bool { 182 if _, blocked := g.visitedNodes[v]; blocked { 183 return false 184 } 185 _, blocked := g.visitedEdges[[2]int64{u, v}] 186 return !blocked 187 } 188 189 func (g yenKSPAdjuster) removeNode(u int64) { 190 g.visitedNodes[u] = struct{}{} 191 } 192 193 func (g yenKSPAdjuster) removeEdge(u, v int64) { 194 g.visitedEdges[[2]int64{u, v}] = struct{}{} 195 if !g.isDirected { 196 g.visitedEdges[[2]int64{v, u}] = struct{}{} 197 } 198 } 199 200 func (g *yenKSPAdjuster) reset() { 201 g.visitedNodes = make(map[int64]struct{}) 202 g.visitedEdges = make(map[[2]int64]struct{}) 203 } 204 205 func (g yenKSPAdjuster) Weight(xid, yid int64) (w float64, ok bool) { 206 return g.weight(xid, yid) 207 }