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

     1  package ch
     2  
     3  import (
     4  	"container/heap"
     5  )
     6  
     7  // ShortestPathManyToMany computes and returns shortest paths and theirs's costs (extended Dijkstra's algorithm) between multiple sources and targets
     8  //
     9  // If there are some errors then function returns '-1.0' as cost and nil as shortest path
    10  //
    11  // sources - set of user's definied IDs of source vertices
    12  // targets - set of user's definied IDs of target vertices
    13  func (graph *Graph) ShortestPathManyToMany(sources, targets []int64) ([][]float64, [][][]int64) {
    14  	endpoints := [directionsCount][]int64{sources, targets}
    15  	for d, directionEndpoints := range endpoints {
    16  		for i, endpoint := range directionEndpoints {
    17  			var ok bool
    18  			if endpoints[d][i], ok = graph.mapping[endpoint]; !ok {
    19  				endpoints[d][i] = -1
    20  			}
    21  		}
    22  	}
    23  	return graph.shortestPathManyToMany(endpoints)
    24  }
    25  
    26  func (graph *Graph) initShortestPathManyToMany(endpointCounts [directionsCount]int) (queryDist [directionsCount][]map[int64]float64, processed [directionsCount][]map[int64]bool, queues [directionsCount][]*vertexDistHeap) {
    27  	for d := forward; d < directionsCount; d++ {
    28  		queryDist[d] = make([]map[int64]float64, endpointCounts[d])
    29  		processed[d] = make([]map[int64]bool, endpointCounts[d])
    30  		queues[d] = make([]*vertexDistHeap, endpointCounts[d])
    31  		for endpointIdx := 0; endpointIdx < endpointCounts[d]; endpointIdx++ {
    32  			queryDist[d][endpointIdx] = make(map[int64]float64)
    33  			processed[d][endpointIdx] = make(map[int64]bool)
    34  			queues[d][endpointIdx] = &vertexDistHeap{}
    35  			heap.Init(queues[d][endpointIdx])
    36  		}
    37  	}
    38  	return
    39  }
    40  
    41  func (graph *Graph) shortestPathManyToMany(endpoints [directionsCount][]int64) ([][]float64, [][][]int64) {
    42  	queryDist, processed, queues := graph.initShortestPathManyToMany([directionsCount]int{len(endpoints[forward]), len(endpoints[backward])})
    43  	for d := forward; d < directionsCount; d++ {
    44  		for endpointIdx, endpoint := range endpoints[d] {
    45  			processed[d][endpointIdx][endpoint] = true
    46  			queryDist[d][endpointIdx][endpoint] = 0
    47  			heapEndpoint := &vertexDist{
    48  				id:   endpoint,
    49  				dist: 0,
    50  			}
    51  			heap.Push(queues[d][endpointIdx], heapEndpoint)
    52  		}
    53  	}
    54  	return graph.shortestPathManyToManyCore(queryDist, processed, queues)
    55  }
    56  
    57  func (graph *Graph) shortestPathManyToManyCore(queryDist [directionsCount][]map[int64]float64, processed [directionsCount][]map[int64]bool, queues [directionsCount][]*vertexDistHeap) ([][]float64, [][][]int64) {
    58  	var prev [directionsCount][]map[int64]int64
    59  	for d := forward; d < directionsCount; d++ {
    60  		prev[d] = make([]map[int64]int64, len(queues[d]))
    61  		for endpointIdx := range queues[d] {
    62  			prev[d][endpointIdx] = make(map[int64]int64)
    63  		}
    64  	}
    65  	estimates := make([][]float64, len(queues[forward]))
    66  	middleIDs := make([][]int64, len(queues[forward]))
    67  	for sourceEndpointIdx := range queues[forward] {
    68  		sourceEstimates := make([]float64, len(queues[backward]))
    69  		sourceMiddleIDs := make([]int64, len(queues[backward]))
    70  		estimates[sourceEndpointIdx] = sourceEstimates
    71  		middleIDs[sourceEndpointIdx] = sourceMiddleIDs
    72  		for targetEndpointIdx := range queues[backward] {
    73  			sourceEstimates[targetEndpointIdx] = Infinity
    74  			sourceMiddleIDs[targetEndpointIdx] = int64(-1)
    75  		}
    76  	}
    77  
    78  	for {
    79  		queuesProcessed := false
    80  		for d := forward; d < directionsCount; d++ {
    81  			reverseDirection := (d + 1) % directionsCount
    82  			for endpointIdx := range queues[d] {
    83  				if queues[d][endpointIdx].Len() == 0 {
    84  					continue
    85  				}
    86  				queuesProcessed = true
    87  				graph.directionalSearchManyToMany(d, endpointIdx, queues[d][endpointIdx], processed[d][endpointIdx], processed[reverseDirection], queryDist[d][endpointIdx], queryDist[reverseDirection], prev[d][endpointIdx], estimates, middleIDs)
    88  			}
    89  		}
    90  		if !queuesProcessed {
    91  			break
    92  		}
    93  	}
    94  	paths := make([][][]int64, len(estimates))
    95  	for sourceEndpointIdx, targetEstimates := range estimates {
    96  		targetPaths := make([][]int64, len(targetEstimates))
    97  		paths[sourceEndpointIdx] = targetPaths
    98  		for targetEndpointIdx, estimate := range targetEstimates {
    99  			if estimate == Infinity {
   100  				targetEstimates[targetEndpointIdx] = -1
   101  				continue
   102  			}
   103  			targetPaths[targetEndpointIdx] = graph.ComputePath(middleIDs[sourceEndpointIdx][targetEndpointIdx], prev[forward][sourceEndpointIdx], prev[backward][targetEndpointIdx])
   104  		}
   105  	}
   106  	return estimates, paths
   107  }
   108  
   109  func (graph *Graph) directionalSearchManyToMany(d direction, endpointIndex int, q *vertexDistHeap, localProcessed map[int64]bool, reverseProcessed []map[int64]bool, localQueryDist map[int64]float64, reverseQueryDist []map[int64]float64, prev map[int64]int64, estimates [][]float64, middleIDs [][]int64) {
   110  
   111  	vertex := heap.Pop(q).(*vertexDist)
   112  	// if vertex.dist <= *estimate { // TODO: move to another place
   113  	localProcessed[vertex.id] = true
   114  	// Edge relaxation in a forward propagation
   115  	var vertexList []incidentEdge
   116  	if d == forward {
   117  		vertexList = graph.Vertices[vertex.id].outIncidentEdges
   118  	} else {
   119  		vertexList = graph.Vertices[vertex.id].inIncidentEdges
   120  	}
   121  	for i := range vertexList {
   122  		temp := vertexList[i].vertexID
   123  		cost := vertexList[i].weight
   124  		if graph.Vertices[vertex.id].orderPos < graph.Vertices[temp].orderPos {
   125  			localDist, ok := localQueryDist[vertex.id]
   126  			if !ok {
   127  				localDist = Infinity
   128  			}
   129  			localDistTemp, ok := localQueryDist[temp]
   130  			if !ok {
   131  				localDistTemp = Infinity
   132  			}
   133  			alt := localDist + cost
   134  			if localDistTemp > alt {
   135  				localQueryDist[temp] = alt
   136  				prev[temp] = vertex.id
   137  				node := &vertexDist{
   138  					id:   temp,
   139  					dist: alt,
   140  				}
   141  				heap.Push(q, node)
   142  			}
   143  		}
   144  	}
   145  	// }
   146  	for revEndpointIdx, revEndpointProcessed := range reverseProcessed {
   147  		if revEndpointProcessed[vertex.id] {
   148  			var sourceEndpoint, targetEndpoint int
   149  			if d == forward {
   150  				sourceEndpoint, targetEndpoint = endpointIndex, revEndpointIdx
   151  			} else {
   152  				targetEndpoint, sourceEndpoint = endpointIndex, revEndpointIdx
   153  			}
   154  			revDist, ok := reverseQueryDist[revEndpointIdx][vertex.id]
   155  			if !ok {
   156  				revDist = Infinity
   157  			}
   158  			if vertex.dist+revDist < estimates[sourceEndpoint][targetEndpoint] {
   159  				middleIDs[sourceEndpoint][targetEndpoint] = vertex.id
   160  				estimates[sourceEndpoint][targetEndpoint] = vertex.dist + revDist
   161  			}
   162  		}
   163  	}
   164  }
   165  
   166  // ShortestPathManyToManyWithAlternatives Computes and returns shortest paths and their cost (extended Dijkstra's algorithm),
   167  // with multiple alternatives for source and target vertices with additional distances to reach the vertices
   168  // (useful if source and target are outside of the graph)
   169  //
   170  // If there are some errors then function returns '-1.0' as cost and nil as shortest path
   171  //
   172  // sourcesAlternatives - set of user's definied IDs of source vertices with additional penalty
   173  // targetsAlternatives - set of user's definied IDs of target vertices  with additional penalty
   174  func (graph *Graph) ShortestPathManyToManyWithAlternatives(sourcesAlternatives, targetsAlternatives [][]VertexAlternative) ([][]float64, [][][]int64) {
   175  	endpoints := [directionsCount][][]VertexAlternative{sourcesAlternatives, targetsAlternatives}
   176  	var endpointsInternal [directionsCount][][]vertexAlternativeInternal
   177  	for d, directionEndpoints := range endpoints {
   178  		endpointsInternal[d] = make([][]vertexAlternativeInternal, 0, len(directionEndpoints))
   179  		for _, alternatives := range directionEndpoints {
   180  			endpointsInternal[d] = append(endpointsInternal[d], graph.vertexAlternativesToInternal(alternatives))
   181  		}
   182  	}
   183  	return graph.shortestPathManyToManyWithAlternatives(endpointsInternal)
   184  }
   185  
   186  func (graph *Graph) shortestPathManyToManyWithAlternatives(endpoints [directionsCount][][]vertexAlternativeInternal) ([][]float64, [][][]int64) {
   187  	queryDist, processed, queues := graph.initShortestPathManyToMany([directionsCount]int{len(endpoints[0]), len(endpoints[1])})
   188  	for d := forward; d < directionsCount; d++ {
   189  		for endpointIdx, endpointAlternatives := range endpoints[d] {
   190  			for _, endpointAlternative := range endpointAlternatives {
   191  				if endpointAlternative.vertexNum == vertexNotFound {
   192  					continue
   193  				}
   194  				processed[d][endpointIdx][endpointAlternative.vertexNum] = true
   195  				queryDist[d][endpointIdx][endpointAlternative.vertexNum] = endpointAlternative.additionalDistance
   196  				heapEndpoint := &vertexDist{
   197  					id:   endpointAlternative.vertexNum,
   198  					dist: endpointAlternative.additionalDistance,
   199  				}
   200  				heap.Push(queues[d][endpointIdx], heapEndpoint)
   201  			}
   202  		}
   203  	}
   204  	return graph.shortestPathManyToManyCore(queryDist, processed, queues)
   205  }