def __init__(self): #_graphLayer[n] is the nth overlay layer #_coveredVertices[n] is the set of vertices in _graphLayer[n] self._graphLayer = [] self._coveredVertices = [] self._metrics = cfg.metrics self._numberOfLayers = None try: #Check if we already have created an overlay graph before. The program can load the file and initialize instantly without #re-creating overlay graphs from the scratch overlayGraphFile = cfg.filelocation["OVERLAYGRAPH"] self._graphLayer = load(overlayGraphFile) self._coveredVertices = map( lambda diGraphAtlayer: set(diGraphAtlayer.vertices()), self._graphLayer) self._numberOfLayers = len(self._graphLayer) applogger.debug("Number of layers in loaded graph = %s", self._numberOfLayers) except IOError: #This occures if a pre-existing overlay graph file doesnt exist on disk. We need to create an overlay graph and store it so that future runs #dont have to do this again. applogger.info( "Creating overlaygraph for the first time. This might take a while" ) self._createOverLayGraph() self._writeOverLayGraphToFile()
def _createOverLayGraph(self): try: #BaseGraph is the raw graph (road network). baseGraphFile = cfg.filelocation["BASEGRAPH"] self._numberOfLayers = cfg.numberOfLayers #initialize graphLayer . ie start with every layer as an empty list. Later on graphLayer[n] will be the nth layer of the overlay graph self._graphLayer = [None for k in xrange(self._numberOfLayers)] self._coveredVertices = [ None for k in xrange(self._numberOfLayers) ] #Initialize the first layer as the raw graph data self._graphLayer[0] = load(baseGraphFile) applogger.debug("Loaded baseGraph") #Initialize the cover in the first layer as all the vertices of the raw graph self._coveredVertices[0] = set(self._graphLayer[0].vertices()) applogger.debug("Retrieved baseGraph vertices") for layerNum in xrange(1, self._numberOfLayers): applogger.debug("Generating Layer %s" % layerNum) self._createLayer(layerNum) applogger.debug( "Layer %s Generated. Number of vertices = %s, Number of edges = %s" % (layerNum, self._graphLayer[layerNum].order(), self._graphLayer[layerNum].size())) applogger.debug("OverlayGraph creation complete") except IOError: print "No base file located" import sys sys.exit()
def _modifyEdgeLabel(self, u, v, layerNum, currentEdgeLabel, newEdgeLabel): modifiedEdgeLabel = currentEdgeLabel.copy() sameWeights = True for metric in newEdgeLabel: #If all the metrices in the new weights are equaivalent to whats already on the edge, we don't have to update the edge if modifiedEdgeLabel.get(metric) != newEdgeLabel.get(metric): sameWeights = false modifiedEdgeLabel[metric] = newEdgeLabel.get(metric) if not sameWeights: applogger.debug("Modifying %s, %s, %s, %s" % (u, v, currentEdgeLabel, layerNum)) self._graphLayer[layerNum].delete_edge(u, v, currentEdgeLabel) self._graphLayer[layerNum].add_edge(u, v, modifiedEdgeLabel) #Sage doesnt support editing in place the edge weight between u,v when there are multiple edges between them #So, I delete the old edge and then add the new edge with new weights(to emulate a modification) else: applogger.debug("Skip Modifying %s, %s, %s, %s" % (u, v, currentEdgeLabel, layerNum))
def _createLayer(self, layerNum): #Find the vertex cover of the graph at the lower layer. Vertex cover works only on for undirected graph in Sage #So, converting the lower layer into a undirected graph and also removing the multiple edges in the lower graph applogger.debug("Starting vertex cover of layer %s" % (layerNum - 1)) self._coveredVertices[layerNum] = self._getVertexCoverOflayer( layerNum - 1) applogger.debug("Retrieved vertex cover of layer %s" % (layerNum - 1)) applogger.debug("VC Size = %s" % len(self._coveredVertices[layerNum])) #This is a switch in-case I decide to store only the shortest path later instead of all routes from one node to another if cfg.ALLPATH: self._graphLayer[layerNum] = self._createAllPathLayer(layerNum)
def findOptimumRoute(self, sourceVertex, targetVertex, userWieghts): """Given two nodes on the graph, returns the optimum path along them @sourceVertex - Starting node @targetVertex - Destination node @UserWeights - Dictionary of user preference eg : {'dist' : 1, 'traffic_lights:0'} will find the optimum path considering shortest distance between source and target {'dist' : 0, 'traffic_lights:1'} will find the optimum path considering least amount of traffic lights between source and target """ applogger.debug("Start of Query") args = [(sourceVertex, True), (targetVertex, False)] pool = ThreadPool(2) #Run a bi-directional dijsktra parallely : A forward Dijsktra from source and a backward dijsktra from the target (forwardResult, backwardWardResult) = pool.map( lambda arg: self._runPartialDijsktra(arg[0], userWieghts, arg[1]), args) #Sample Forward Result : #({1: {'cost': 0, 'parent': None}, 2: {'cost': 1, 'parent': 1}, 3: {'cost': 2, 'parent': 2}, 4: {'cost': 3, 'parent': 3}, 10: {'cost': 1, 'parent': 1}}, set([10, 4])) #Sample BackWard Result : #({8: {'cost': 0, 'parent': None}, 6: {'cost': 2, 'parent': 7}, 7: {'cost': 1, 'parent': 8}}, set([6])) pool.close() pool.join() verticesVisitedDuringForwardSearch, forwardAccessNodes = forwardResult verticesVisitedDuringBackwardSearch, backWardAccessNodes = backwardWardResult #TargetVertex was already settled during the forward search. We have nothing more to do here. if targetVertex in verticesVisitedDuringForwardSearch: path, _ = self._getNodesAlongShortestPath( targetVertex, verticesVisitedDuringForwardSearch) path.reverse() jsonDict = { "minCost": verticesVisitedDuringForwardSearch.get(targetVertex).get( "cost"), "route": path } return jsonDict #To run, Dijsktra on the overlay graph, we initialize the priority Q with every access node obtained in the forward #search and its cost from source. By the end of the Dijstra in Overlay graph, we get the cost from source to every #access node of the target forwardAccessNodePathCost = [ (accessNode, verticesVisitedDuringForwardSearch.get(accessNode).get("cost")) for accessNode in forwardAccessNodes ] verticesVisitedDuringOverlaySearch = self._runDijsktraOnOverlay( forwardAccessNodePathCost, backWardAccessNodes.copy(), userWieghts) #Sample verticesVisitedDuringOverlaySearch : #{10: {'cost': 1, 'intermediateVerticesToParent': [], 'parent': None}, 4: {'cost': 3, 'intermediateVerticesToParent': [], 'parent': None}, 6: {'cost': 4, 'intermediateVerticesToParent': [], 'parent': 4}} #Pick the target access node that minimizes the cost of (source to access node) + (access node to target) distToTargetViaAcessNodes = map( lambda accessNode: (verticesVisitedDuringOverlaySearch[ accessNode].get("cost") + verticesVisitedDuringBackwardSearch[ accessNode].get("cost"), accessNode), backWardAccessNodes) minimumCost, minimumAccessNode = min(distToTargetViaAcessNodes) #Here , we create the shortest route from the source to target. #For , overlay search, we use the parent heirarchy and the edgeIntermediate vertices to retrieve the actual path pathAlongOverLayGraph, lastNode = self._getNodesAlongShortestPath( minimumAccessNode, verticesVisitedDuringOverlaySearch, True) pathAlongForwardSearch, _ = self._getNodesAlongShortestPath( lastNode, verticesVisitedDuringForwardSearch, False) pathAlongBackWardSearch, _ = self._getNodesAlongShortestPath( minimumAccessNode, verticesVisitedDuringBackwardSearch, False) path = [] path.extend(pathAlongOverLayGraph) path.extend(pathAlongForwardSearch) #The parent heirarchy lists the route in reverse order. So we reverse it to obtain the forward order path.reverse() #Backward search operated in the backward direction. So the parent heirarchy will already list vertices in the actual forward order #So, no need to reverse it path.extend(pathAlongBackWardSearch) applogger.debug("End of Query") #print path jsonDict = {"minCost": minimumCost, "route": path} return jsonDict