github.com/LdDl/ch@v1.7.8/contraction.go (about) 1 package ch 2 3 import ( 4 "container/heap" 5 "fmt" 6 "time" 7 ) 8 9 var ( 10 tmLayout = "2006-01-2T15:04:05.999999999" 11 ) 12 13 // Preprocess Computes contraction hierarchies and returns node ordering 14 func (graph *Graph) Preprocess(pqImportance *importanceHeap) { 15 extractionOrder := int64(0) 16 for pqImportance.Len() != 0 { 17 // Lazy update heuristic: 18 // update Importance of vertex "on demand" as follows: 19 // Before contracting vertex with currently smallest Importance, recompute its Importance and see if it is still the smallest 20 // If not pick next smallest one, recompute its Importance and see if that is the smallest now; If not, continue in same way ... 21 vertex := heap.Pop(pqImportance).(*Vertex) 22 vertex.computeImportance() 23 if pqImportance.Len() != 0 && vertex.importance > pqImportance.Peek().importance { 24 pqImportance.Push(vertex) 25 continue 26 } 27 vertex.orderPos = extractionOrder 28 graph.contractNode(vertex) 29 if graph.verbose { 30 if extractionOrder > 0 && pqImportance.Len()%1000 == 0 { 31 fmt.Printf("Contraction Order: %d / %d, Remain vertices in heap: %d. Currect shortcuts num: %d Initial edges num: %d Time: %v\n", extractionOrder, len(graph.Vertices), pqImportance.Len(), graph.shortcutsNum, graph.edgesNum, time.Now().Format(tmLayout)) 32 } 33 } 34 extractionOrder++ 35 } 36 } 37 38 // markNeighbors 39 // 40 // inEdges Incoming edges from vertex 41 // outEdges Outcoming edges from vertex 42 // 43 func (graph *Graph) markNeighbors(inEdges, outEdges []incidentEdge) { 44 for i := range inEdges { 45 temp := inEdges[i] 46 graph.Vertices[temp.vertexID].delNeighbors++ 47 } 48 for i := range outEdges { 49 temp := outEdges[i] 50 graph.Vertices[temp.vertexID].delNeighbors++ 51 } 52 } 53 54 // contractNode 55 // 56 // vertex Vertex to be contracted 57 // 58 func (graph *Graph) contractNode(vertex *Vertex) { 59 // Consider all vertices with edges incoming TO current vertex as U 60 incomingEdges := vertex.inIncidentEdges 61 62 // Consider all vertices with edges incoming FROM current vertex as W 63 outcomingEdges := vertex.outIncidentEdges 64 65 // Exclude vertex for local shortest paths searches 66 vertex.contracted = true 67 // Tell neighbor vertices that current vertex has been contracted 68 graph.markNeighbors(incomingEdges, outcomingEdges) 69 70 // For every vertex 'w' in W, compute Pw as the cost from 'u' to 'w' through current vertex, which is the sum of the edge weights w(u, vertex) + w(vertex, w). 71 inMax := 0.0 72 outMax := 0.0 73 for i := range incomingEdges { 74 if graph.Vertices[incomingEdges[i].vertexID].contracted { 75 continue 76 } 77 if inMax < incomingEdges[i].weight { 78 inMax = incomingEdges[i].weight 79 } 80 } 81 for i := range outcomingEdges { 82 if graph.Vertices[outcomingEdges[i].vertexID].contracted { 83 continue 84 } 85 if outMax < outcomingEdges[i].weight { 86 outMax = outcomingEdges[i].weight 87 } 88 } 89 // Then Pmax is the maximum pMax over all 'w' in W. 90 pmax := inMax + outMax 91 92 // Perform a standard Dijkstra’s shortest path search from 'u' on the subgraph excluding current vertex. 93 graph.processIncidentEdges(vertex, pmax) 94 } 95 96 // processIncidentEdges Returns evaluated shorcuts 97 // 98 // vertex - Vertex for making possible shortcuts around 99 // pmax - path cost restriction 100 // 101 func (graph *Graph) processIncidentEdges(vertex *Vertex, pmax float64) { 102 incomingEdges := vertex.inIncidentEdges 103 outcomingEdges := vertex.outIncidentEdges 104 if len(outcomingEdges) == 0 { 105 return 106 } 107 batchShortcuts := make([]ShortcutPath, 0) 108 109 previousOrderPos := int64(vertex.orderPos - 1) 110 for _, u := range incomingEdges { 111 inVertex := u.vertexID 112 // Do not consider any vertex has been excluded earlier 113 if graph.Vertices[inVertex].contracted { 114 continue 115 } 116 inCost := u.weight 117 graph.shortestPathsWithMaxCost(inVertex, pmax, previousOrderPos) // Finds the shortest distances from the inVertex to all outVertices. 118 for _, w := range outcomingEdges { 119 outVertex := w.vertexID 120 outVertexPtr := graph.Vertices[outVertex] 121 // Do not consider any vertex has been excluded earlier 122 if outVertexPtr.contracted { 123 continue 124 } 125 outCost := w.weight 126 neighborsWeights := inCost + outCost 127 // For each w, if dist(u, w) > Pw we add a shortcut edge uw with weight Pw. 128 // If this condition doesn’t hold, no shortcut is added. 129 if outVertexPtr.distance.distance > neighborsWeights || 130 outVertexPtr.distance.previousOrderPos != previousOrderPos || // Optional condition: if previous shortestPathsWithMaxCost(...) call has changed shortest path tree 131 outVertexPtr.distance.previousSourceID != inVertex { // Optional condition: if previous shortestPathsWithMaxCost(...) call has changed shortest path tree 132 133 // Collect needed shortcuts 134 batchShortcuts = append(batchShortcuts, ShortcutPath{From: inVertex, To: outVertex, Via: vertex.vertexNum, Cost: neighborsWeights}) 135 } 136 } 137 } 138 graph.insertShortcuts(batchShortcuts) 139 } 140 141 // insertShortcuts Creates (or updates: it depends on conditions) multiple shortcuts in graph structure 142 func (graph *Graph) insertShortcuts(shortcuts []ShortcutPath) { 143 for i := range shortcuts { 144 d := shortcuts[i] 145 graph.createOrUpdateShortcut(d.From, d.To, d.Via, d.Cost) 146 } 147 } 148 149 // createOrUpdateShortcut Creates (or updates: it depends on conditions) shortcut 150 // 151 // fromVertex - Library defined ID of source vertex where shortcut starts from 152 // fromVertex - Library defined ID of target vertex where shortcut leads to 153 // viaVertex - Library defined ID of vertex through which the shortcut exists 154 // summaryCost - Travel path of a shortcut 155 // 156 func (graph *Graph) createOrUpdateShortcut(fromVertex, toVertex, viaVertex int64, summaryCost float64) { 157 if _, ok := graph.shortcuts[fromVertex]; !ok { 158 // If there is no such shortcut then add one. 159 graph.shortcuts[fromVertex] = make(map[int64]*ShortcutPath) 160 } 161 if existing, ok := graph.shortcuts[fromVertex][toVertex]; !ok { 162 // Prepare shorcut pointer if there is no From-To-Via combo 163 graph.shortcuts[fromVertex][toVertex] = &ShortcutPath{ 164 From: fromVertex, 165 To: toVertex, 166 Via: viaVertex, 167 Cost: summaryCost, 168 } 169 graph.Vertices[fromVertex].addOutIncidentEdge(toVertex, summaryCost) 170 graph.Vertices[toVertex].addInIncidentEdge(fromVertex, summaryCost) 171 graph.shortcutsNum++ 172 } else { 173 // If shortcut already exists 174 if summaryCost < existing.Cost { 175 // If middle vertex is not optimal for shortcut then change cost 176 existing.Cost = summaryCost 177 updatedOutSuccess := graph.Vertices[fromVertex].updateOutIncidentEdge(toVertex, summaryCost) 178 if !updatedOutSuccess { 179 panic(fmt.Sprintf("Should not happen [1]. Can't update outcoming incident edge. %d has no common edge with %d", fromVertex, toVertex)) 180 } 181 updatedInSuccess := graph.Vertices[toVertex].updateInIncidentEdge(fromVertex, summaryCost) 182 if !updatedInSuccess { 183 panic(fmt.Sprintf("Should not happen [2]. Can't update incoming incident edge. %d has no common edge with %d", toVertex, fromVertex)) 184 } 185 // We should check if the middle vertex is still the same 186 // We could just do existing.ViaVertex = viaVertex, but it could be helpful for debugging purposes. 187 if existing.Via != viaVertex { 188 existing.Via = viaVertex 189 } 190 } 191 } 192 }