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  }