def update_edge_properties(self, lowerBoundTime, interval): """ Updates the edge properties after computation of a new flowInterval :param lowerBoundTime: lowerBoundTime of flowInterval :param interval: flowInterval """ for v, w in self.network.edges(): # Inflow changes vTimeLower, vTimeUpper = self.node_label( v, interval.lowerBoundTime), self.node_label( v, interval.upperBoundTime) inflowChangeBool = Utilities.is_not_eq_tol( interval.NTFNodeLabelDict[v], 0) # Can we extend the inflow interval? inflowVal = interval.NTFEdgeFlowDict[ (v, w)] / interval.NTFNodeLabelDict[v] if inflowChangeBool else 0 if inflowChangeBool: self.network[v][w]['inflow'][(vTimeLower, vTimeUpper)] = inflowVal if vTimeUpper < float('inf'): vLastTime = next( reversed(self.network[v][w]['cumulativeInflow'])) self.network[v][w]['cumulativeInflow'][ vTimeUpper] = self.network[v][w]['cumulativeInflow'][ vLastTime] + inflowVal * (vTimeUpper - vTimeLower) # Outflow changes wTimeLower, wTimeUpper = self.node_label( w, interval.lowerBoundTime), self.node_label( w, interval.upperBoundTime) outflowChangeBool = Utilities.is_not_eq_tol( interval.NTFNodeLabelDict[w], 0) # Can we extend the outflow interval? outflowVal = interval.NTFEdgeFlowDict[ (v, w)] / interval.NTFNodeLabelDict[w] if outflowChangeBool else 0 if outflowChangeBool: self.network[v][w]['outflow'][(wTimeLower, wTimeUpper)] = outflowVal if wTimeUpper < float('inf'): wLastTime = next( reversed(self.network[v][w]['cumulativeOutflow'])) self.network[v][w]['cumulativeOutflow'][ wTimeUpper] = self.network[v][w]['cumulativeOutflow'][ wLastTime] + outflowVal * (wTimeUpper - wTimeLower) # Queue size changes if vTimeUpper < float('inf'): self.update_queue_size(v, w, vTimeLower, vTimeUpper) self.animationIntervals[(v, w)].append( ((vTimeLower, vTimeUpper), (wTimeLower, wTimeUpper))) if vTimeUpper <= wTimeUpper and vTimeUpper != float('inf'): # Lies on shortest path self.network[v][w]['load'][vTimeUpper] = self.arc_load( v, w, vTimeUpper)
def time_interval_correspondence(self, t): """ :param t: timepoint :return: lastTime: lowerBound of flowInterval containing time """ if Utilities.is_eq_tol(t, 0): return 0 for lowerBoundTime in self.lowerBoundsToIntervalDict: if Utilities.is_geq_tol(t, lowerBoundTime): lastTime = lowerBoundTime return lastTime
def __init__(self, network, resettingEdges, lowerBoundTime, inflowRate, minCapacity, counter, outputDirectory, templateFile, scipFile, timeout): """ :param network: Networkx Digraph instance :param resettingEdges: list of resetting edges :param lowerBoundTime: \theta_k :param inflowRate: u_0 :param minCapacity: minimum capacity of all edges in network :param counter: unique ID of FlowInterval - needed for directory creation :param outputDirectory: directory to output scip logs :param templateFile: path of template which is used by SCIP :param scipFile: path to scip binary :param timeout: seconds until timeout. Deactivated if equal to 0 """ self.network = network self.resettingEdges = resettingEdges self.lowerBoundTime = lowerBoundTime # theta_k self.upperBoundTime = None # theta_{k+1} = theta_k + self.alpha self.inflowRate = inflowRate self.minCapacity = minCapacity # Needed for zimpl files self.id = counter self.outputDirectory = outputDirectory self.templateFile = templateFile self.scipFile = scipFile self.timeout = timeout self.foundNTF = False self.aborted = False self.alpha = None self.shortestPathNetwork = None # to be set from NashFlowClass self.numberOfSolvedIPs = 0 self.computationalTime = -1 # Elapsed computation time in seconds self.preprocessedNodes = 0 self.preprocessedEdges = 0 self.NTFNodeLabelDict = {node: 0 for node in self.network} self.NTFEdgeFlowDict = {edge: 0 for edge in self.network.edges()} self.binaryVariableNumberList = [ ] # List where each element is the size of E'_0 in a NTF call # Create FlowInterval Directory self.rootPath = os.path.join( self.outputDirectory, str(self.id) + '-FlowInterval-' + Utilities.get_time()) Utilities.create_dir(self.rootPath) if self.timeout > 0: # Start thread controlling whether computation should be aborted self.timeoutThread = threading.Thread(target=self.timeout_control) self.timeoutThread.start()
def get_outflow(self, v, w, t): """ :param v: tail of edge :param w: head of edge :param t: time :return: f_(v,w)^-(t), i.e. outflow rate of e=(v,w) at time t (picking the newest value) """ if Utilities.is_eq_tol(t, 0): return 0 for wTimeLower, wTimeUpper in self.network[v][w]['outflow']: if Utilities.is_between_tol(wTimeLower, t, wTimeUpper): # t lies between l_w(theta_k) and l_w(theta_k+1) lastOutflow = self.network[v][w]['outflow'][(wTimeLower, wTimeUpper)] return lastOutflow
def __init__(self, graph, inflowRate, numberOfIntervals, outputDirectory, templateFile, scipFile, cleanUpBool, timeout): """ :param graph: Networkx Digraph instance :param inflowRate: u_0 :param numberOfIntervals: number of intervals that will be computed. -1 if all :param outputDirectory: path where output should be saved :param templateFile: Selected method, i.e. 0,1,2 :param scipFile: path to scip binary :param cleanUpBool: If true, then cleanup :param timeout: seconds until timeout. Deactivated if equal to 0 """ self.network = graph.copy() self.inflowRate = inflowRate # For the moment: constant self.numberOfIntervals = numberOfIntervals # No. of intervals to compute self.outputDirectory = outputDirectory # Template File from /source/templates self.templateFile = os.path.join( os.getcwd(), 'source', 'templates', 'algorithm_' + str(templateFile + 1) + '.zpl') self.allInOne = (templateFile == 1) self.advancedAlgo = ( templateFile == 2 ) # If true, then advanced backtracking with preprocessing is performed self.scipFile = scipFile self.cleanUpBool = cleanUpBool self.numberOfSolvedIPs = 0 self.computationalTime = 0 self.infinityReached = False # True if last interval has alpha = +inf self.timeout = timeout self.minCapacity = Utilities.compute_min_attr_of_network(self.network) self.counter = 0 self.preprocessedNodes = 0 self.preprocessedEdges = 0 # Create directory for Nash-Flow self.rootPath = os.path.join(self.outputDirectory, 'NashFlow-' + Utilities.get_time()) Utilities.create_dir(self.rootPath) self.flowIntervals = [ ] # List containing triplets of form (lowerBound, upperBound, FlowInterval-instance) self.lowerBoundsToIntervalDict = OrderedDict() self.animationIntervals = {edge: [] for edge in self.network.edges()}
def get_cumulative_outflow(self, v, w, t): """ :param v: tail of edge :param w: head of edge :param t: time :return: F_(v,w)^-(t) """ if Utilities.is_leq_tol(t, 0): return 0 for timeInterval, outflowVal in reversed( self.network[v][w]['outflow'].items()): wTimeLower, wTimeUpper = timeInterval if Utilities.is_between_tol(wTimeLower, t, wTimeUpper): # This is the interval in which t lies return self.network[v][w]['cumulativeOutflow'][ wTimeLower] + outflowVal * (t - wTimeLower)
def open_nfc(self, moveGraph=None): """ Opens NashFlowComputation Tool :param moveGraph: network that should be moved, None if not specified :return: """ if not moveGraph: # Just open the application cmd = ['python', '../mainControl.py'] else: # Open the application with a certain graph # Save the graph tmpDir = gettempdir() tmpFileName = Utilities.get_time() tmpFilePath = os.path.join(tmpDir, tmpFileName) self.save_graph(graphPath=tmpFilePath) tmpFilePathArgument = tmpFilePath + '.cg' cmd = ['python', '../mainControl.py', '-l', tmpFilePathArgument] def open_nfc_thread(): self.proc = subprocess.Popen(cmd) self.proc.communicate() thread = threading.Thread(target=open_nfc_thread) thread.start()
def draw_edges(self, G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, **kwds): """Workaround to call specific edge drawing function""" edges, boxes, tubes, arrows = Utilities.draw_animation_edges(G, pos, edgelist, width, edge_color, style, alpha, edge_cmap, edge_vmin, edge_vmax, ax, arrows, label) self.tube_collection = tubes return edges, boxes, arrows
def run_order(self): """Execution order""" self.rootPath = os.path.join( self.outputDirectory, str(self.id) + '-NTF-' + Utilities.get_time()) Utilities.create_dir(self.rootPath) copy(self.templateFile, self.rootPath) self.templateFile = os.path.join(self.rootPath, os.path.basename(self.templateFile)) self.logFile = os.path.join(self.rootPath, 'outputLog.txt') self.write_zimpl_files() self.start_process() self.check_result()
def run(self, nextIntervalOnly=False): """ Compute the flow intervals up to self.numberOfIntervals :param nextIntervalOnly: If True, only the next flow interval is computed """ computedUpperBound = 0 k = 1 if self.numberOfIntervals != -1 else -float('inf') if nextIntervalOnly: Utilities.create_dir(self.rootPath) self.compute_flowInterval() else: while computedUpperBound < float( 'inf') and k <= self.numberOfIntervals: self.compute_flowInterval() computedUpperBound = self.flowIntervals[-1][1] k += 1 # Clean up if self.cleanUpBool: rmtree(self.rootPath)
def is_full(self, v, w, t): """ :param v: tail of edge :param w: head of edge :param t: time :return: True if d_(v,w)(t) ~= storage(v,w) """ try: load = self.arc_load(v, w, t) return Utilities.is_eq_tol(load, self.network[v][w]['storage']) except TypeError: return False
def assert_ntf(self): """Check if computed NTF really is an NTF""" # Works only on shortest path network p = lambda e: max( [self.NTFNodeLabelDict[e[0]], self.NTFEdgeFlowDict[e] / self.network[e[0]][e[1]]['outCapacity']]) \ if e not in self.resettingEdges \ else self.NTFEdgeFlowDict[e] / self.network[e[0]][e[1]]['outCapacity'] for w in self.shortestPathNetwork: if self.shortestPathNetwork.in_edges(w): minimalCongestion = min( map(p, self.shortestPathNetwork.in_edges(w))) assert (Utilities.is_eq_tol(minimalCongestion, self.NTFNodeLabelDict[w])) for v, w in self.shortestPathNetwork.edges(): minimalCongestion = min( map(p, self.shortestPathNetwork.in_edges(w))) assert (Utilities.is_eq_tol(self.NTFEdgeFlowDict[v, w], 0) or Utilities.is_eq_tol(p((v, w)), minimalCongestion)) # Check if actually an s-t-flow for w in self.shortestPathNetwork: m = 0 incomingEdges = self.shortestPathNetwork.in_edges(w) outgoingEdges = self.shortestPathNetwork.out_edges(w) for e in incomingEdges: m += self.NTFEdgeFlowDict[e] for e in outgoingEdges: m -= self.NTFEdgeFlowDict[e] if w == 's': assert (Utilities.is_eq_tol(m, (-1) * self.inflowRate)) elif w == 't': assert (Utilities.is_eq_tol(m, self.inflowRate)) else: assert (Utilities.is_eq_tol(m, 0))
def change_edge_show_status(self, show=True): """ Change whether zero-flow edges are visible or not """ if show: self.network = self.originalNetwork.copy() # self.network = deepcopy(self.originalNetwork) else: removedEdges = [] for edge in self.network.edges(): if Utilities.is_eq_tol(self.NTFEdgeFlowDict[edge], 0): removedEdges.append(edge) self.network.remove_edges_from(removedEdges) isolated_nodes = list(nx.isolates(self.network)) self.network.remove_nodes_from(isolated_nodes) self.init_plot()
def draw_nodes(G, pos, nodelist=None, node_size=300, node_color='r', node_shape='o', alpha=1.0, cmap=None, vmin=None, vmax=None, ax=None, linewidths=None, label=None, **kwds): """Workaround to specify node drawing function""" return Utilities.draw_nodes(G, pos, nodelist, node_size, node_color, node_shape, alpha, cmap, vmin, vmax, ax, linewidths, label)
def add_hline_to_plots(self): """Add horizontal lines to intersect vertical line""" for hLine in self.hLines: hLine.remove() self.hLines = [] self.lineToHLineDict = dict() for label in self.hLinesLabels: label.remove() self.hLinesLabels = [] for plot in self.plots: xVals, yVals, line, label = plot # Get the y-value of self.verticalLinePos index = Utilities.get_insertion_point_left(xVals, self.verticalLinePos) if index == len(xVals) or index == 0: continue x1, x, x2 = xVals[index - 1], self.verticalLinePos, xVals[index] y1, y2 = yVals[index - 1], yVals[index] # It holds xVals[index-1] < self.verticalLinePos <= xVals[index] fac = float(x - x2) / (x1 - x2) y = fac * y1 + (1 - fac) * y2 # this obviously only works if plots are piecewise linear axes = self.figure.gca() lowerBound, upperBound = axes.get_xlim() lineBeginFac = float(x - lowerBound) / (upperBound - lowerBound) hLine = axes.axhline(y=y, xmin=lineBeginFac, xmax=1, linewidth=self.verticalLineWidth) hLine.set_color(self.verticalLineColor) self.hLines.append(hLine) hLineText = axes.text(1.02, y, "%.2f" % y, va='center', ha="left", bbox=dict(facecolor="w", alpha=0.5), transform=axes.get_yaxis_transform()) hLineText.set_fontsize(8) self.hLinesLabels.append(hLineText) if not line.get_visible(): hLine.set_visible(False) hLineText.set_visible(False) self.lineToHLineDict[line] = (hLine, hLineText)
def draw_edges(self, G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, **kwds): """Workaround to specify edge drawing function""" arrows = self.showArrows return Utilities.draw_edges_with_boxes(G, pos, edgelist, width, edge_color, style, alpha, edge_cmap, edge_vmin, edge_vmax, ax, arrows, label)
def get_approx_data(self, t): """ Returns approximated queue size and load at time t :param t: time :return: z_e(t), d_e(t), where entries can be "N/A" """ D = {'Queue size': "N/A", 'Load': "N/A"} for plot in self.plots: xVals, yVals, line, label = plot if label != 'Load' and label != "Queue size": continue index = Utilities.get_insertion_point_left(xVals, t) if index == len(xVals) or index == 0: continue else: x1, x, x2 = xVals[index - 1], t, xVals[index] y1, y2 = yVals[index - 1], yVals[index] # It holds: xVals[index-1] < t <= xVals[index] fac = float(x - x2) / (x1 - x2) y = fac * y1 + (1 - fac) * y2 # this obviously only works if plots are piecewise linear D[label] = y return D['Queue size'], D['Load']
def get_edge_labels(self): """Returns dict of edge labels""" if self.type == 'general': attributes = ['outCapacity', 'transitTime' ] if not self.onlyNTF else ['outCapacity'] multipleBool = (len(attributes) > 1) elif self.type == 'spillback': attributes = ['inCapacity', 'outCapacity', 'storage', 'transitTime'] if not self.onlyNTF \ else ['outCapacity'] # 'inflowBound' has to be added manually below multipleBool = True attributeList = [ nx.get_edge_attributes(self.network, attr) for attr in attributes ] if (self.type == 'spillback' and self.onlyNTF): attr = 'inflowBound' d = dict() for e in self.network.edges(): (v, w) = e d[e] = self.network[v][w]['TFC'][attr] attributeList.append(d) labelDict = Utilities.join_intersect_dicts(*attributeList) # Integer values should be displayed in shortest possible way for key, valTuple in labelDict.items(): if not multipleBool: if valTuple != float('inf') and valTuple == int(valTuple): labelDict[key] = int(valTuple) else: newTuple = [] for val in valTuple: newTuple.append( int(val) if ( val != float('inf') and int(val) == val) else val) labelDict[key] = tuple(newTuple) return labelDict
def update_edges(self, added=False, removal=False, moved=False, color=False): """ Redraw edges(s) :param added: If True then an edge has been added :param removal: If True then an edge has been removed :param moved: If True then an edge has been moved :param color: If True then the color of an edge has changed """ if removal: # Edges have been deleted collectionIndex = 0 toDeleteIndices = [] for edges, edgeCollection in self.edgeCollections: missingEdges = [ edge for edge in edges if edge not in self.network.edges() ] if missingEdges: boxCollection = self.boxCollections[collectionIndex][1] arrowCollection = self.arrowCollections[collectionIndex][1] edgeCollection.remove() boxCollection.remove() arrowCollection.remove() edges = [ edge for edge in edges if edge not in missingEdges ] if edges: positions = { v: self.network.nodes[v]['position'] for v in self.network.nodes() } newEdgeCollection, newBoxCollection, newArrowCollection = self.draw_edges( self.network, pos=positions, ax=self.axes, arrow=True, edgelist=edges, width=self.edgeWidthSize) self.edgeCollections[collectionIndex] = ( edges, newEdgeCollection) self.boxCollections[collectionIndex] = ( edges, newBoxCollection) self.arrowCollections[collectionIndex] = ( edges, newArrowCollection) else: toDeleteIndices.append(collectionIndex) # Delete edge labels for edge in missingEdges: deletedLabel = self.edgeLabelCollection.pop(edge) deletedLabel.remove() collectionIndex += 1 for index in reversed(toDeleteIndices): del self.edgeCollections[index] del self.boxCollections[index] del self.arrowCollections[index] elif added: # A node has been added (can we do better than plotting all nodes again) if self.focusEdge is not None: v, w = self.focusEdge edgeCollection, boxCollection, arrowCollection = self.draw_edges( self.network, pos={ v: self.network.nodes[v]['position'], w: self.network.nodes[w]['position'] }, ax=self.axes, arrow=True, edgelist=[self.focusEdge], width=self.edgeWidthSize) self.edgeCollections.append(([self.focusEdge], edgeCollection)) self.boxCollections.append(([self.focusEdge], boxCollection)) self.arrowCollections.append( ([self.focusEdge], arrowCollection)) edgeLabelSize = int(round(self.edgeLabelFontSize)) if not self.onlyNTF: if self.type == 'general': lbl = { self.focusEdge: (self.network[v][w]['outCapacity'], self.network[v][w]['transitTime']) } elif self.type == 'spillback': lbl = { self.focusEdge: (self.network[v][w]['inCapacity'], self.network[v][w]['outCapacity'], self.network[v][w]['transitTime'], self.network[v][w]['storage']) } else: if self.type == 'general': lbl = { self.focusEdge: (self.network[v][w]['outCapacity']) } elif self.type == 'spillback': lbl = { self.focusEdge: (self.network[v][w]['outCapacity'], self.network[v][w]['TFC']['inflowBound']) } self.edgeLabelCollection.update( draw_networkx_edge_labels( self.network, pos={ v: self.network.nodes[v]['position'], w: self.network.nodes[w]['position'] }, ax=self.axes, edge_labels=lbl, font_size=edgeLabelSize)) elif moved: collectionIndex = 0 for edges, edgeCollection in self.edgeCollections: pos = nx.get_node_attributes(self.network, 'position') p = 0.3 edge_pos = [] for edge in edges: src, dst = np.array(pos[edge[0]]), np.array(pos[edge[1]]) s = dst - src # src = src + p * s # Box at beginning # dst = src + (1 - p) * s # Box at the end dst = src # No edge at all edge_pos.append((src, dst)) edge_pos = np.asarray(edge_pos) box_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edges]) # Move edges edgeCollection.set_segments(edge_pos) boxCollection = self.boxCollections[collectionIndex][1] # Move boxes boxCollection.remove() boxCollection = Utilities.get_boxes(edge_pos=box_pos) boxCollection.set_zorder(1) # edges go behind nodes # boxCollection.set_label(label) self.axes.add_collection(boxCollection) self.boxCollections[collectionIndex] = ( self.boxCollections[collectionIndex][0], boxCollection) arrowCollection = self.arrowCollections[collectionIndex][1] # Move boxes arrowCollection.remove() arrowCollection = Utilities.get_arrows_on_edges( edge_pos=box_pos) arrowCollection.set_zorder(2) # edges go behind nodes # boxCollection.set_label(label) self.axes.add_collection(arrowCollection) self.arrowCollections[collectionIndex] = ( self.arrowCollections[collectionIndex][0], arrowCollection) collectionIndex += 1 if color: # Update colors edgeSize = lambda v, w: self.edgeWidthSize if ( v, w) != self.focusEdge else self.edgeWidthSize + 1 boxSize = lambda v, w: 1 if (v, w) != self.focusEdge else 2 collectionIndex = 0 for edges, edgeCollection in self.edgeCollections: if edges: edgeColorList = [ colorConverter.to_rgba(self.edgeColor(v, w), 1) for v, w in edges ] edgeCollection.set_color(edgeColorList) edgeCollection.set_linewidth( [edgeSize(v, w) for v, w in edges]) boxCollection = self.boxCollections[collectionIndex][1] boxCollection.set_edgecolor(edgeColorList) boxCollection.set_linewidth( [boxSize(v, w) for v, w in edges]) arrowCollection = self.arrowCollections[collectionIndex][1] arrowCollection.set_edgecolor(edgeColorList) collectionIndex += 1 # Update edge label texts and positions lbls = self.get_edge_labels() for edge, label in self.edgeLabelCollection.items( ): # type(label) = matplotlib.text.Text object v, w = edge if self.focusNode is not None: if v not in self.focusNode and w not in self.focusNode: # No need to update anything continue lblTuple = lbls[(v, w)] if label.get_text() != lblTuple: label.set_text(lblTuple) posv = (self.network.nodes[v]['position'][0] * 0.5, self.network.nodes[v]['position'][1] * 0.5) posw = (self.network.nodes[w]['position'][0] * 0.5, self.network.nodes[w]['position'][1] * 0.5) pos = (posv[0] + posw[0], posv[1] + posw[1]) label.set_position(pos) rotAngle = Utilities.get_edge_label_rotation( self.axes, posv, posw, pos) label.set_rotation(rotAngle) self.draw_idle()
def draw_flow(self, edge, src, dst): """ Draw flow animation :param edge: edge = vw to draw :param src: position of v :param dst: position of w """ if self.edgeColoring[edge]: for fk in self.edgeColoring[edge].keys(): self.edgeColoring[edge][fk].remove() self.edgeColoring[edge].clear() if self.widthReferenceSize[edge]: self.widthReferenceSize[edge].clear() if self.visualQueueColoring[edge]: self.visualQueueColoring.remove() v, w = edge time = self.timePoints[self.currentTimeIndex] transitTime = self.network[v][w]['transitTime'] src = np.array(src) dst = np.array(dst) s = dst - src flowOnEdgeList = [self.flowOnEntireEdge[edge][fk][time] for fk in range(len(self.nashFlow.flowIntervals))] totalFlowOnEdge = sum(flowOnEdgeList) if Utilities.is_eq_tol(totalFlowOnEdge, 0): return overflowBlocks = [] for fk in range(len(self.nashFlow.flowIntervals)): if Utilities.is_eq_tol(self.flowOnEdgeNotQueue[edge][fk][time], 0): # No flow on edgeNotQueue at this time continue inflowInterval, outflowInterval = self.nashFlow.animationIntervals[edge][fk] vTimeLower, vTimeUpper = inflowInterval inflow = float(self.network[v][w]['inflow'][(vTimeLower, vTimeUpper)]) # These position factors are using that the amount on the edgeNotQueue has to be positive at this point # This implies that vTimeLower <= time <= vTimeUpper + transitTime startFac = float(time - vTimeUpper) / transitTime if time > vTimeUpper else 0 endFac = float(time - vTimeLower) / transitTime if time <= vTimeLower + transitTime else 1 start = src + startFac * s end = src + endFac * s edge_pos = np.asarray([(start, end)]) edge_color = tuple(colorConverter.to_rgba(self.NTFColors[fk % len(self.NTFColors)], alpha=1)) widthRatioScale = min(1, inflow/self.maxWidthFlowSize[edge]) self.widthReferenceSize[edge][fk] = max(self.tubeWidthMaximum*widthRatioScale, self.tubeWidthMinimum) # Drawing of flow tubes edgeCollection = LineCollection(edge_pos, colors=edge_color, linewidths=self.tubeWidthFactor*self.widthReferenceSize[edge][fk], antialiaseds=(1,), transOffset=self.axes.transData, alpha=1 ) edgeCollection.set_zorder(1) self.edgeColoring[edge][fk] = edgeCollection self.axes.add_collection(edgeCollection) # Drawing of overflow blocks ''' if not overflowBlocks: if Utilities.is_eq_tol(self.flowOnQueue[edge][fk][time], 0): continue else: # Draw first block blockSizeFactor = min(1, self.flowOnEntireEdge[edge][fk][time]/self.maxOverflowStorage[edge]) block = Rectangle(start - delta, width=d, height=14, transform=t, facecolor=self.NTFColors[fk % len(self.NTFColors)], linewidth=None, alpha=1) overflowBlocks.append(block) else: pass ''' if overflowBlocks: overflowBlockCollection = PatchCollection(boxes, match_original=True, antialiaseds=(1,), transOffset=self.axes.transData) self.visualQueueColoring[edge] = overflowBlockCollection self.axes.add_collection(overflowBlockCollection)
def compute_NTF(self): """Computes NTF in current tab""" originalNetwork = self.gttr('network') network = deepcopy(originalNetwork) # Remove inactive edges from network inactiveEdges = [ edge for edge in originalNetwork.edges() if not originalNetwork[edge[0]][edge[1]]['TFC']['active'] ] network.remove_edges_from(inactiveEdges) # Remove nodes that are now no longer reachable L = [] for (v, d) in network.in_degree(): if d == 0 and v != 's': # Non-reachable node found L.append(v) network.remove_nodes_from(L) # Validate input returnCode = self.validate_thinflow_input(network) if returnCode != 0: # Invalid input has been given # Spawn warning QtWidgets.QMessageBox.question(QtWidgets.QWidget(), 'Abort: Input error', self.get_error_message(returnCode), QtWidgets.QMessageBox.Ok) return # Drop current NTF plot self.re_init_NTF_frame() # Get necessary data resettingEdges = [ edge for edge in network.edges() if network[edge[0]][edge[1]]['TFC']['resettingEnabled'] ] lowerBoundTime = 0 # No needed for different times as only one flowInterval is being computed inflowRate = float(self.inflowLineEdit.text()) minCapacity = Utilities.compute_min_attr_of_network(network) counter = "Standalone" rootPath = self.outputDirectory self.templateFile = self.templateComboBox.currentIndex() if self.currentTF == 'general': templateFile = os.path.join( os.getcwd(), 'templates', 'algorithm_' + str(self.templateFile + 1) + '.zpl') elif self.currentTF == 'spillback': print( "Note: For spillback only the basic algorithm [1] is available and hence run now." ) templateFile = os.path.join(os.getcwd(), 'templates', 'algorithm_spillback_1.zpl') scipFile = self.scipFile timeout = float(self.timeoutLineEdit.text()) self.save_config() if self.currentTF == 'general': self.interval_general = FlowInterval(network, resettingEdges=resettingEdges, lowerBoundTime=lowerBoundTime, inflowRate=inflowRate, minCapacity=minCapacity, counter=counter, outputDirectory=rootPath, templateFile=templateFile, scipFile=scipFile, timeout=timeout) elif self.currentTF == 'spillback': fullEdges = [] minInflowBound = float('inf') for e in network.edges(): (v, w) = e minInflowBound = min(minInflowBound, network[v][w]['TFC']['inflowBound']) self.interval_spillback = FlowInterval_spillback( network, resettingEdges=resettingEdges, fullEdges=fullEdges, lowerBoundTime=lowerBoundTime, inflowRate=inflowRate, minCapacity=minCapacity, counter=counter, outputDirectory=rootPath, templateFile=templateFile, scipFile=scipFile, timeout=timeout, minInflowBound=minInflowBound) # Set shortest path network manually to entire graph (is the deepcopy really needed?) interval = self.gttr('interval') interval.shortestPathNetwork = deepcopy(network) if self.currentTF == 'spillback': interval.transfer_inflowBound(interval.shortestPathNetwork) self.advancedAlgo = ( self.templateFile == 2 ) # If true, then advanced backtracking with preprocessing is performed if self.currentTF == 'general': if self.advancedAlgo: interval.get_ntf_advanced() else: interval.get_ntf() elif self.currentTF == 'spillback': interval.get_ntf() self.sttr( 'plotNTFCanvas', self.currentTF, PlotNTFCanvas( interval.shortestPathNetwork, self, intervalID=None, stretchFactor=self.plotNTFCanvasStretchFactor, showNoFlowEdges=self.showEdgesWithoutFlowCheckBox.isChecked(), onlyNTF=True)) self.gttr('plotNTFFrameLayout').addWidget(self.gttr('plotNTFCanvas')) self.cleanup()
def update_edge_properties(self, lowerBoundTime, interval): """ Updates the edge properties after computation of a new flowInterval :param lowerBoundTime: lowerBoundTime of flowInterval :param interval: flowInterval """ if lowerBoundTime == 0: # init in/outflow for v, w in self.network.edges(): vTimeLower = self.node_label(v, 0) wTimeLower = self.node_label(w, 0) self.network[v][w]['inflow'] = OrderedDict() self.network[v][w]['inflow'][(0, vTimeLower)] = 0 self.network[v][w]['outflow'] = OrderedDict() self.network[v][w]['outflow'][(0, wTimeLower)] = 0 self.network[v][w]['cumulativeInflow'] = OrderedDict() self.network[v][w]['cumulativeInflow'][0] = 0 self.network[v][w]['cumulativeInflow'][vTimeLower] = 0 self.network[v][w]['cumulativeOutflow'] = OrderedDict() self.network[v][w]['cumulativeOutflow'][0] = 0 self.network[v][w]['cumulativeOutflow'][wTimeLower] = 0 self.network[v][w]['queueSize'] = OrderedDict() self.network[v][w]['queueSize'][0] = 0 self.network[v][w]['queueSize'][ vTimeLower + self.network[v][w]['transitTime']] = 0 for v, w in self.network.edges(): # Inflow changes vTimeLower, vTimeUpper = self.node_label( v, interval.lowerBoundTime), self.node_label( v, interval.upperBoundTime) inflowChangeBool = Utilities.is_not_eq_tol( interval.NTFNodeLabelDict[v], 0) # Can we extend the inflow interval? inflowVal = interval.NTFEdgeFlowDict[ (v, w)] / interval.NTFNodeLabelDict[v] if inflowChangeBool else 0 if inflowChangeBool: self.network[v][w]['inflow'][(vTimeLower, vTimeUpper)] = inflowVal if vTimeUpper < float('inf'): vLastTime = next( reversed(self.network[v][w]['cumulativeInflow'])) self.network[v][w]['cumulativeInflow'][ vTimeUpper] = self.network[v][w]['cumulativeInflow'][ vLastTime] + inflowVal * (vTimeUpper - vTimeLower) # Outflow changes wTimeLower, wTimeUpper = self.node_label( w, interval.lowerBoundTime), self.node_label( w, interval.upperBoundTime) outflowChangeBool = Utilities.is_not_eq_tol( interval.NTFNodeLabelDict[w], 0) # Can we extend the outflow interval? outflowVal = interval.NTFEdgeFlowDict[ (v, w)] / interval.NTFNodeLabelDict[w] if outflowChangeBool else 0 if outflowChangeBool: self.network[v][w]['outflow'][(wTimeLower, wTimeUpper)] = outflowVal if wTimeUpper < float('inf'): wLastTime = next( reversed(self.network[v][w]['cumulativeOutflow'])) self.network[v][w]['cumulativeOutflow'][ wTimeUpper] = self.network[v][w]['cumulativeOutflow'][ wLastTime] + outflowVal * (wTimeUpper - wTimeLower) # Queue size changes if vTimeUpper < float('inf'): lastQueueSizeTime = next( reversed(self.network[v][w]['queueSize'])) lastQueueSize = self.network[v][w]['queueSize'][ lastQueueSizeTime] self.network[v][w]['queueSize'][ vTimeUpper + self.network[v][w]['transitTime']] = max( 0, lastQueueSize + (inflowVal - self.network[v][w]['outCapacity']) * (vTimeUpper - vTimeLower)) self.animationIntervals[(v, w)].append( ((vTimeLower, vTimeUpper), (wTimeLower, wTimeUpper)))
def update_queue_animation(self, radius=7): """Update queue animation to display different edge queue""" if not self.focusEdge: return if self.boxColoring: self.boxColoring.remove() self.boxColoring = None # Work setting edge = self.focusEdge time = self.timePoints[self.currentTimeIndex] totalFlowOnQueue = sum( self.flowOnQueue[edge][fk][time] for fk in range(len(self.nashFlow.flowIntervals))) if Utilities.is_eq_tol(totalFlowOnQueue, 0) or Utilities.is_eq_tol( self.maxQueueSize, 0): return flowRatio = [ max(0, float(self.flowOnQueue[edge][fk][time]) / totalFlowOnQueue) for fk in range(len(self.nashFlow.flowIntervals)) ] totalRatio = totalFlowOnQueue / float(self.maxQueueSize) delta = np.array([0, radius]) src = np.array(self.src) dst = np.array(self.dst) s = dst - src angle = np.rad2deg(np.arctan2(s[1], s[0])) t = matplotlib.transforms.Affine2D().rotate_deg_around( src[0], src[1], angle) boxes = [] lastProportion = 1 for fk in range(len(self.nashFlow.flowIntervals)): if Utilities.is_eq_tol(flowRatio[fk], 0): continue d = np.sqrt(np.sum(((dst - src) * lastProportion)**2)) rec = Rectangle(src - delta, width=d, height=radius * 2, transform=t, facecolor=self.NTFColors[fk % len(self.NTFColors)], linewidth=int(lastProportion), alpha=1) boxes.append(rec) lastProportion -= (totalRatio * flowRatio[fk]) d = np.sqrt(np.sum(((dst - src) * (1 - totalRatio))**2)) lastRec = Rectangle(src - delta, width=d, height=radius * 2, transform=t, facecolor='white', linewidth=0, alpha=1) boxes.append(lastRec) boxCollection = PatchCollection(boxes, match_original=True, antialiaseds=(1, ), transOffset=self.axes.transData) self.boxColoring = boxCollection self.axes.add_collection(boxCollection)
def compute_flowInterval(self): """Method to compute a single flowInterval""" # Get lowerBoundTime lowerBoundTime = 0 if not self.flowIntervals else self.flowIntervals[ -1][1] # Compute resettingEdges cmp_queue = lambda v, w, t: \ Utilities.is_greater_tol(self.node_label(w, t), self.node_label(v, t) + self.network[v][w]['transitTime']) resettingEdges = [(v, w) for v, w in self.network.edges() if cmp_queue(v, w, lowerBoundTime)] \ if lowerBoundTime > 0 else [] edges_to_choose_from = self.network.edges( ) # Fulledges can be non resetting! fullEdges = [(v, w) for v, w in edges_to_choose_from if self.is_full(v, w, self.node_label(v, lowerBoundTime)) ] if lowerBoundTime > 0 else [] minInflowBound = None interval = FlowInterval_spillback(self.network, resettingEdges=resettingEdges, fullEdges=fullEdges, lowerBoundTime=lowerBoundTime, inflowRate=self.inflowRate, minCapacity=self.minOutCapacity, counter=self.counter, outputDirectory=self.rootPath, templateFile=self.templateFile, scipFile=self.scipFile, timeout=self.timeout, minInflowBound=minInflowBound) if lowerBoundTime == 0: interval.shortestPathNetwork = Utilities.get_shortest_path_network( self.network) # Compute shortest path network for v, w in interval.shortestPathNetwork.edges(): interval.shortestPathNetwork[v][w][ 'inflowBound'] = self.network[v][w]['inCapacity'] else: interval.shortestPathNetwork = Utilities.get_shortest_path_network( self.network, labels={ v: self.node_label(v, lowerBoundTime) for v in self.network }) # Compute shortest path network for v, w in interval.shortestPathNetwork.edges(): vTimeLower = self.node_label(v, lowerBoundTime) minimizer = self.get_outflow( v, w, vTimeLower) if (v, w) in fullEdges else float('inf') interval.shortestPathNetwork[v][w]['inflowBound'] = min( minimizer, self.network[v][w]['inCapacity']) minInflowBound = Utilities.compute_min_attr_of_network( interval.shortestPathNetwork, 'inflowBound') interval.set_minInflowBound(minInflowBound) start = time.time() interval.get_ntf() ''' if self.advancedAlgo: interval.get_ntf_advanced() else: interval.get_ntf() ''' end = time.time() self.computationalTime += (end - start) interval.computationalTime = end - start self.preprocessedNodes += interval.preprocessedNodes self.preprocessedEdges += interval.preprocessedEdges self.lowerBoundsToIntervalDict[lowerBoundTime] = interval if lowerBoundTime == 0: self.init_edge_properties() interval.compute_alpha({ node: self.node_label(node, lowerBoundTime) for node in self.network }) self.flowIntervals.append( (interval.lowerBoundTime, interval.upperBoundTime, interval)) # Update in/out-flow rates self.update_edge_properties(lowerBoundTime, interval) self.counter += 1 self.numberOfSolvedIPs += interval.numberOfSolvedIPs self.infinityReached = (interval.alpha == float('inf'))
def compute_flowInterval(self): """Method to compute a single flowInterval""" # Get lowerBoundTime lowerBoundTime = 0 if not self.flowIntervals else self.flowIntervals[ -1][1] # Compute resettingEdges resettingEdges = [(v, w) for v, w in self.network.edges() if Utilities.is_greater_tol( self.node_label(w, lowerBoundTime), self.node_label(v, lowerBoundTime) + self.network[v][w]['transitTime'], TOL) ] if lowerBoundTime > 0 else [] interval = FlowInterval( self.network, resettingEdges=resettingEdges, lowerBoundTime=lowerBoundTime, inflowRate=self.inflowRate, minCapacity=self.minCapacity, counter=self.counter, outputDirectory=self.rootPath, templateFile=self.templateFile, scipFile=self.scipFile, timeout=self.timeout, ) if lowerBoundTime == 0: interval.shortestPathNetwork = Utilities.get_shortest_path_network( self.network) # Compute shortest path network else: interval.shortestPathNetwork = Utilities.get_shortest_path_network( self.network, labels={ v: self.node_label(v, lowerBoundTime) for v in self.network }) # Compute shortest path network start = time.time() if self.advancedAlgo: interval.get_ntf_advanced() else: interval.get_ntf() end = time.time() self.computationalTime += (end - start) interval.computationalTime = end - start self.preprocessedNodes += interval.preprocessedNodes self.preprocessedEdges += interval.preprocessedEdges self.lowerBoundsToIntervalDict[lowerBoundTime] = interval interval.compute_alpha({ node: self.node_label(node, lowerBoundTime) for node in self.network }) self.flowIntervals.append( (interval.lowerBoundTime, interval.upperBoundTime, interval)) # Update in/out-flow rates self.update_edge_properties(lowerBoundTime, interval) self.counter += 1 self.numberOfSolvedIPs += interval.numberOfSolvedIPs self.infinityReached = (interval.alpha == float('inf'))