github.com/LdDl/ch@v1.7.8/bidirectional_ch_one_to_n.go (about)

     1  package ch
     2  
     3  import (
     4  	"container/heap"
     5  )
     6  
     7  func (graph *Graph) initShortestPathOneToMany() (estimateAll []float64, pathAll [][]int64, prev map[int64]int64, prevReverse map[int64]int64, queryDist, revQueryDist []float64, forwProcessed, revProcessed []int64) {
     8  	estimateAll = []float64{}
     9  	pathAll = [][]int64{}
    10  
    11  	prev = make(map[int64]int64)
    12  	prevReverse = make(map[int64]int64)
    13  
    14  	queryDist = make([]float64, len(graph.Vertices))
    15  	revQueryDist = make([]float64, len(graph.Vertices))
    16  
    17  	forwProcessed = make([]int64, len(graph.Vertices))
    18  	revProcessed = make([]int64, len(graph.Vertices))
    19  
    20  	return
    21  }
    22  
    23  // ShortestPathOneToMany computes and returns shortest paths and theirs's costs (extended Dijkstra's algorithm) between single source and multiple targets
    24  //
    25  // If there are some errors then function returns '-1.0' as cost and nil as shortest path
    26  //
    27  // source - user's definied ID of source vertex
    28  // targets - set of user's definied IDs of target vertices
    29  func (graph *Graph) ShortestPathOneToMany(source int64, targets []int64) ([]float64, [][]int64) {
    30  	estimateAll, pathAll, prev, prevReverse, queryDist, revQueryDist, forwProcessed, revProcessed := graph.initShortestPathOneToMany()
    31  
    32  	var ok bool
    33  	if source, ok = graph.mapping[source]; !ok {
    34  		estimateAll = append(estimateAll, -1.0)
    35  		pathAll = append(pathAll, nil)
    36  		return estimateAll, pathAll
    37  	}
    38  
    39  	for idx, target := range targets {
    40  		nextQueue := int64(idx) + 1
    41  		if source == target {
    42  			estimateAll = append(estimateAll, 0)
    43  			pathAll = append(pathAll, []int64{source})
    44  			continue
    45  		}
    46  		var ok bool
    47  		if target, ok = graph.mapping[target]; !ok {
    48  			estimateAll = append(estimateAll, -1.0)
    49  			pathAll = append(pathAll, nil)
    50  			continue
    51  		}
    52  
    53  		forwProcessed[source] = nextQueue
    54  		revProcessed[target] = nextQueue
    55  
    56  		queryDist[source] = 0
    57  		revQueryDist[target] = 0
    58  
    59  		forwQ := &vertexDistHeap{}
    60  		backwQ := &vertexDistHeap{}
    61  
    62  		heap.Init(forwQ)
    63  		heap.Init(backwQ)
    64  
    65  		heapSource := &vertexDist{
    66  			id:   source,
    67  			dist: 0,
    68  		}
    69  		heapTarget := &vertexDist{
    70  			id:   target,
    71  			dist: 0,
    72  		}
    73  
    74  		heap.Push(forwQ, heapSource)
    75  		heap.Push(backwQ, heapTarget)
    76  
    77  		estimate, path := graph.shortestPathOneToManyCore(nextQueue, prev, prevReverse, queryDist, revQueryDist, forwProcessed, revProcessed, forwQ, backwQ)
    78  
    79  		estimateAll = append(estimateAll, estimate)
    80  		pathAll = append(pathAll, path)
    81  	}
    82  
    83  	return estimateAll, pathAll
    84  }
    85  
    86  func (graph *Graph) shortestPathOneToManyCore(nextQueue int64, prev map[int64]int64, prevReverse map[int64]int64, queryDist, revQueryDist []float64, forwProcessed, revProcessed []int64, forwQ *vertexDistHeap, backwQ *vertexDistHeap) (float64, []int64) {
    87  	estimate := Infinity
    88  
    89  	var middleID int64
    90  
    91  	for forwQ.Len() != 0 || backwQ.Len() != 0 {
    92  		if forwQ.Len() != 0 {
    93  			vertex1 := heap.Pop(forwQ).(*vertexDist)
    94  			if vertex1.dist <= estimate {
    95  				forwProcessed[vertex1.id] = nextQueue
    96  				graph.relaxEdgesBiForwardOneToMany(vertex1, forwQ, prev, queryDist, nextQueue, forwProcessed)
    97  			}
    98  			if revProcessed[vertex1.id] == nextQueue {
    99  				if vertex1.dist+revQueryDist[vertex1.id] < estimate {
   100  					middleID = vertex1.id
   101  					estimate = vertex1.dist + revQueryDist[vertex1.id]
   102  				}
   103  			}
   104  		}
   105  
   106  		if backwQ.Len() != 0 {
   107  			vertex2 := heap.Pop(backwQ).(*vertexDist)
   108  			if vertex2.dist <= estimate {
   109  				revProcessed[vertex2.id] = nextQueue
   110  				graph.relaxEdgesBiBackwardOneToMany(vertex2, backwQ, prevReverse, revQueryDist, nextQueue, revProcessed)
   111  			}
   112  
   113  			if forwProcessed[vertex2.id] == nextQueue {
   114  				if vertex2.dist+queryDist[vertex2.id] < estimate {
   115  					middleID = vertex2.id
   116  					estimate = vertex2.dist + queryDist[vertex2.id]
   117  				}
   118  			}
   119  		}
   120  
   121  	}
   122  	if estimate == Infinity {
   123  		return -1, nil
   124  	}
   125  
   126  	return estimate, graph.ComputePath(middleID, prev, prevReverse)
   127  }
   128  
   129  // ShortestPathOneToManyWithAlternatives Computes and returns shortest path and it's cost (extended Dijkstra's algorithm) between single source and multiple targets
   130  // with multiple alternatives for source and target vertices with additional distances to reach the vertices
   131  // (useful if source and target are outside of the graph)
   132  //
   133  // If there are some errors then function returns '-1.0' as cost and nil as shortest path
   134  //
   135  // sourceAlternatives - user's definied ID of source vertex with additional penalty
   136  // targetsAlternatives - set of user's definied IDs of target vertices  with additional penalty
   137  func (graph *Graph) ShortestPathOneToManyWithAlternatives(sourceAlternatives []VertexAlternative, targetsAlternatives [][]VertexAlternative) ([]float64, [][]int64) {
   138  	estimateAll, pathAll, prev, prevReverse, queryDist, revQueryDist, forwProcessed, revProcessed := graph.initShortestPathOneToMany()
   139  
   140  	sourceAlternativesInternal := graph.vertexAlternativesToInternal(sourceAlternatives)
   141  
   142  	for idx, targetAlternatives := range targetsAlternatives {
   143  		nextQueue := int64(idx) + 1
   144  
   145  		targetAlternativesInternal := graph.vertexAlternativesToInternal(targetAlternatives)
   146  
   147  		forwQ := &vertexDistHeap{}
   148  		backwQ := &vertexDistHeap{}
   149  
   150  		heap.Init(forwQ)
   151  		heap.Init(backwQ)
   152  
   153  		for _, sourceAlternative := range sourceAlternativesInternal {
   154  			if sourceAlternative.vertexNum == vertexNotFound {
   155  				continue
   156  			}
   157  			forwProcessed[sourceAlternative.vertexNum] = nextQueue
   158  			queryDist[sourceAlternative.vertexNum] = sourceAlternative.additionalDistance
   159  
   160  			heapSource := &vertexDist{
   161  				id:   sourceAlternative.vertexNum,
   162  				dist: sourceAlternative.additionalDistance,
   163  			}
   164  			heap.Push(forwQ, heapSource)
   165  		}
   166  		for _, targetAlternative := range targetAlternativesInternal {
   167  			if targetAlternative.vertexNum == vertexNotFound {
   168  				continue
   169  			}
   170  			revProcessed[targetAlternative.vertexNum] = nextQueue
   171  			revQueryDist[targetAlternative.vertexNum] = targetAlternative.additionalDistance
   172  
   173  			heapTarget := &vertexDist{
   174  				id:   targetAlternative.vertexNum,
   175  				dist: targetAlternative.additionalDistance,
   176  			}
   177  			heap.Push(backwQ, heapTarget)
   178  		}
   179  
   180  		estimate, path := graph.shortestPathOneToManyCore(nextQueue, prev, prevReverse, queryDist, revQueryDist, forwProcessed, revProcessed, forwQ, backwQ)
   181  
   182  		estimateAll = append(estimateAll, estimate)
   183  		pathAll = append(pathAll, path)
   184  	}
   185  
   186  	return estimateAll, pathAll
   187  }
   188  
   189  func (graph *Graph) relaxEdgesBiForwardOneToMany(vertex *vertexDist, forwQ *vertexDistHeap, prev map[int64]int64, queryDist []float64, cid int64, forwProcessed []int64) {
   190  	vertexList := graph.Vertices[vertex.id].outIncidentEdges
   191  	for i := range vertexList {
   192  		temp := vertexList[i].vertexID
   193  		cost := vertexList[i].weight
   194  		if graph.Vertices[vertex.id].orderPos < graph.Vertices[temp].orderPos {
   195  			alt := queryDist[vertex.id] + cost
   196  			if forwProcessed[temp] != cid || queryDist[temp] > alt {
   197  				queryDist[temp] = alt
   198  				prev[temp] = vertex.id
   199  				forwProcessed[temp] = cid
   200  				node := &vertexDist{
   201  					id:   temp,
   202  					dist: alt,
   203  				}
   204  				heap.Push(forwQ, node)
   205  			}
   206  		}
   207  	}
   208  }
   209  
   210  func (graph *Graph) relaxEdgesBiBackwardOneToMany(vertex *vertexDist, backwQ *vertexDistHeap, prev map[int64]int64, revQueryDist []float64, cid int64, revProcessed []int64) {
   211  	vertexList := graph.Vertices[vertex.id].inIncidentEdges
   212  	for i := range vertexList {
   213  		temp := vertexList[i].vertexID
   214  		cost := vertexList[i].weight
   215  		if graph.Vertices[vertex.id].orderPos < graph.Vertices[temp].orderPos {
   216  			alt := revQueryDist[vertex.id] + cost
   217  			if revProcessed[temp] != cid || revQueryDist[temp] > alt {
   218  				revQueryDist[temp] = alt
   219  				prev[temp] = vertex.id
   220  				revProcessed[temp] = cid
   221  				node := &vertexDist{
   222  					id:   temp,
   223  					dist: alt,
   224  				}
   225  				heap.Push(backwQ, node)
   226  			}
   227  		}
   228  	}
   229  }