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