github.com/gopherd/gonum@v0.0.4/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  	"sort"
     9  
    10  	"github.com/gopherd/gonum/graph"
    11  	"github.com/gopherd/gonum/graph/iterator"
    12  )
    13  
    14  // YenKShortestPaths returns the k-shortest loopless paths from s to t in g.
    15  // YenKShortestPaths will panic if g contains a negative edge weight.
    16  func YenKShortestPaths(g graph.Graph, k int, s, t graph.Node) [][]graph.Node {
    17  	// See https://en.wikipedia.org/wiki/Yen's_algorithm and
    18  	// the paper at https://doi.org/10.1090%2Fqam%2F253822.
    19  
    20  	_, isDirected := g.(graph.Directed)
    21  	yk := yenKSPAdjuster{
    22  		Graph:      g,
    23  		isDirected: isDirected,
    24  	}
    25  
    26  	if wg, ok := g.(Weighted); ok {
    27  		yk.weight = wg.Weight
    28  	} else {
    29  		yk.weight = UniformCost(g)
    30  	}
    31  
    32  	shortest, _ := DijkstraFrom(s, yk).To(t.ID())
    33  	switch len(shortest) {
    34  	case 0:
    35  		return nil
    36  	case 1:
    37  		return [][]graph.Node{shortest}
    38  	}
    39  	paths := [][]graph.Node{shortest}
    40  
    41  	var pot []yenShortest
    42  	var root []graph.Node
    43  	for i := int64(1); i < int64(k); i++ {
    44  		for n := 0; n < len(paths[i-1])-2; n++ {
    45  			yk.reset()
    46  
    47  			spur := paths[i-1][n]
    48  			root := append(root[:0], paths[i-1][:n+1]...)
    49  
    50  			for _, path := range paths {
    51  				if len(path) <= n {
    52  					continue
    53  				}
    54  				ok := true
    55  				for x := 0; x < len(root); x++ {
    56  					if path[x].ID() != root[x].ID() {
    57  						ok = false
    58  						break
    59  					}
    60  				}
    61  				if ok {
    62  					yk.removeEdge(path[n].ID(), path[n+1].ID())
    63  				}
    64  			}
    65  
    66  			spath, weight := DijkstraFrom(spur, yk).To(t.ID())
    67  			if len(root) > 1 {
    68  				var rootWeight float64
    69  				for x := 1; x < len(root); x++ {
    70  					w, _ := yk.weight(root[x-1].ID(), root[x].ID())
    71  					rootWeight += w
    72  				}
    73  				root = append(root[:len(root)-1], spath...)
    74  				pot = append(pot, yenShortest{root, weight + rootWeight})
    75  			} else {
    76  				pot = append(pot, yenShortest{spath, weight})
    77  			}
    78  		}
    79  
    80  		if len(pot) == 0 {
    81  			break
    82  		}
    83  
    84  		sort.Sort(byPathWeight(pot))
    85  		best := pot[0].path
    86  		if len(best) <= 1 {
    87  			break
    88  		}
    89  		paths = append(paths, best)
    90  		pot = pot[1:]
    91  	}
    92  
    93  	return paths
    94  }
    95  
    96  // yenShortest holds a path and its weight for sorting.
    97  type yenShortest struct {
    98  	path   []graph.Node
    99  	weight float64
   100  }
   101  
   102  type byPathWeight []yenShortest
   103  
   104  func (s byPathWeight) Len() int           { return len(s) }
   105  func (s byPathWeight) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   106  func (s byPathWeight) Less(i, j int) bool { return s[i].weight < s[j].weight }
   107  
   108  // yenKSPAdjuster allows walked edges to be omitted from a graph
   109  // without altering the embedded graph.
   110  type yenKSPAdjuster struct {
   111  	graph.Graph
   112  	isDirected bool
   113  
   114  	// weight is the edge weight function
   115  	// used for shortest path calculation.
   116  	weight Weighting
   117  
   118  	// visitedEdges holds the edges that have
   119  	// been removed by Yen's algorithm.
   120  	visitedEdges map[[2]int64]struct{}
   121  }
   122  
   123  func (g yenKSPAdjuster) From(id int64) graph.Nodes {
   124  	nodes := graph.NodesOf(g.Graph.From(id))
   125  	for i := 0; i < len(nodes); {
   126  		if g.canWalk(id, nodes[i].ID()) {
   127  			i++
   128  			continue
   129  		}
   130  		nodes[i] = nodes[len(nodes)-1]
   131  		nodes = nodes[:len(nodes)-1]
   132  	}
   133  	return iterator.NewOrderedNodes(nodes)
   134  }
   135  
   136  func (g yenKSPAdjuster) canWalk(u, v int64) bool {
   137  	_, ok := g.visitedEdges[[2]int64{u, v}]
   138  	return !ok
   139  }
   140  
   141  func (g yenKSPAdjuster) removeEdge(u, v int64) {
   142  	g.visitedEdges[[2]int64{u, v}] = struct{}{}
   143  	if g.isDirected {
   144  		g.visitedEdges[[2]int64{v, u}] = struct{}{}
   145  	}
   146  }
   147  
   148  func (g *yenKSPAdjuster) reset() {
   149  	g.visitedEdges = make(map[[2]int64]struct{})
   150  }
   151  
   152  func (g yenKSPAdjuster) Weight(xid, yid int64) (w float64, ok bool) {
   153  	return g.weight(xid, yid)
   154  }