gonum.org/v1/gonum@v0.14.0/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  			pot = append(pot, yenShortest{spath, weight})
    89  		}
    90  
    91  		if len(pot) == 0 {
    92  			break
    93  		}
    94  
    95  		sort.Sort(byPathWeight(pot))
    96  		best := pot[0]
    97  		if len(best.path) <= 1 || best.weight > cost {
    98  			break
    99  		}
   100  		paths = append(paths, best.path)
   101  		pot = pot[1:]
   102  	}
   103  
   104  	return paths
   105  }
   106  
   107  // yenShortest holds a path and its weight for sorting.
   108  type yenShortest struct {
   109  	path   []graph.Node
   110  	weight float64
   111  }
   112  
   113  type byPathWeight []yenShortest
   114  
   115  func (s byPathWeight) Len() int           { return len(s) }
   116  func (s byPathWeight) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   117  func (s byPathWeight) Less(i, j int) bool { return s[i].weight < s[j].weight }
   118  
   119  // yenKSPAdjuster allows walked edges to be omitted from a graph
   120  // without altering the embedded graph.
   121  type yenKSPAdjuster struct {
   122  	graph.Graph
   123  	isDirected bool
   124  
   125  	// weight is the edge weight function
   126  	// used for shortest path calculation.
   127  	weight Weighting
   128  
   129  	// visitedNodes holds the nodes that have
   130  	// been removed by Yen's algorithm.
   131  	visitedNodes map[int64]struct{}
   132  
   133  	// visitedEdges holds the edges that have
   134  	// been removed by Yen's algorithm.
   135  	visitedEdges map[[2]int64]struct{}
   136  }
   137  
   138  func (g yenKSPAdjuster) From(id int64) graph.Nodes {
   139  	if _, blocked := g.visitedNodes[id]; blocked {
   140  		return graph.Empty
   141  	}
   142  	nodes := graph.NodesOf(g.Graph.From(id))
   143  	for i := 0; i < len(nodes); {
   144  		if g.canWalk(id, nodes[i].ID()) {
   145  			i++
   146  			continue
   147  		}
   148  		nodes[i] = nodes[len(nodes)-1]
   149  		nodes = nodes[:len(nodes)-1]
   150  	}
   151  	if len(nodes) == 0 {
   152  		return graph.Empty
   153  	}
   154  	return iterator.NewOrderedNodes(nodes)
   155  }
   156  
   157  func (g yenKSPAdjuster) canWalk(u, v int64) bool {
   158  	if _, blocked := g.visitedNodes[v]; blocked {
   159  		return false
   160  	}
   161  	_, blocked := g.visitedEdges[[2]int64{u, v}]
   162  	return !blocked
   163  }
   164  
   165  func (g yenKSPAdjuster) removeNode(u int64) {
   166  	g.visitedNodes[u] = struct{}{}
   167  }
   168  
   169  func (g yenKSPAdjuster) removeEdge(u, v int64) {
   170  	g.visitedEdges[[2]int64{u, v}] = struct{}{}
   171  	if !g.isDirected {
   172  		g.visitedEdges[[2]int64{v, u}] = struct{}{}
   173  	}
   174  }
   175  
   176  func (g *yenKSPAdjuster) reset() {
   177  	g.visitedNodes = make(map[int64]struct{})
   178  	g.visitedEdges = make(map[[2]int64]struct{})
   179  }
   180  
   181  func (g yenKSPAdjuster) Weight(xid, yid int64) (w float64, ok bool) {
   182  	return g.weight(xid, yid)
   183  }