def to_graphics(A): G = MultiDiGraph() G.add_nodes_from(A.vertices()) for u, v in A.edges(): for a, b, d in A.weights(u, v): G.add_edge(u, v, label=a + "->" + b + "," + d) return G
def to_directed(self): """Return a directed representation of the graph. Returns ------- G : MultiDiGraph A directed graph with the same name, same nodes, and with each edge (u,v,data) replaced by two directed edges (u,v,data) and (v,u,data). Notes ----- This returns a "deepcopy" of the edge, node, and graph attributes which attempts to completely copy all of the data and references. This is in contrast to the similar D=DiGraph(G) which returns a shallow copy of the data. See the Python copy module for more information on shallow and deep copies, http://docs.python.org/library/copy.html. Warning ------- If you have subclassed MultiGraph to use dict-like objects in the data structure, those changes do not transfer to the MultiDiGraph created by this method. Examples -------- >>> G = nx.Graph() # or MultiGraph, etc >>> G.add_path([0,1]) >>> H = G.to_directed() >>> H.edges() [(0, 1), (1, 0)] If already directed, return a (deep) copy >>> G = nx.DiGraph() # or MultiDiGraph, etc >>> G.add_path([0,1]) >>> H = G.to_directed() >>> H.edges() [(0, 1)] """ from networkx.classes.multidigraph import MultiDiGraph G=MultiDiGraph() G.add_nodes_from(self) G.add_edges_from( (u,v,key,deepcopy(datadict)) for u,nbrs in self.adjacency_iter() for v,keydict in nbrs.items() for key,datadict in keydict.items() ) G.graph=deepcopy(self.graph) G.node=deepcopy(self.node) return G
def to_subgraph(self, begin, end, multigraph=False, edge_data=False, edge_interval_data=False, node_data=False): """Return a networkx DiGraph or MultiDiGraph which includes all the nodes and edges which have overlapping intervals with the given interval. Parameters ---------- begin: int or float Inclusive beginning time of the edge appearing in the interval graph. end: int or float Non-inclusive ending time of the edge appearing in the interval graph. Must be bigger than or equal to begin. multigraph: bool, optional (default= False) If True, a networkx MultiGraph will be returned. If False, networkx Graph. edge_data: bool, optional (default= False) If True, edges will keep their attributes. edge_interval_data: bool, optional (default= False) If True, each edge's attribute will also include its begin and end interval data. If `edge_data= True` and there already exist edge attributes with names begin and end, they will be overwritten. node_data : bool, optional (default= False) If True, each node's attributes will be included. See Also -------- to_snapshots : divide the interval graph to snapshots Notes ----- If multigraph= False, and edge_data=True or edge_interval_data=True, in case there are multiple edges, only one will show with one of the edge's attributes. Note: nodes with no edges will not appear in any subgraph. Examples -------- >>> G = dnx.IntervalGraph() >>> G.add_edges_from([(1, 2, 3, 10), (2, 4, 1, 11), (6, 4, 12, 19), (2, 4, 8, 15)]) >>> H = G.to_subgraph(4, 12) >>> type(H) <class 'networkx.classes.graph.DiGraph'> >>> list(H.edges(data=True)) [(1, 2, {}), (2, 4, {})] >>> H = G.to_subgraph(4, 12, edge_interval_data=True) >>> type(H) <class 'networkx.classes.graph.DiGraph'> >>> list(H.edges(data=True)) [(1, 2, {'end': 10, 'begin': 3}), (2, 4, {'end': 15, 'begin': 8})] >>> M = G.to_subgraph(4, 12, multigraph=True, edge_interval_data=True) >>> type(M) <class 'networkx.classes.multigraph.MultiDiGraph'> >>> list(M.edges(data=True)) [(1, 2, {'end': 10, 'begin': 3}), (2, 4, {'end': 11, 'begin': 1}), (2, 4, {'end': 15, 'begin': 8})] """ if begin and end and begin > end: raise NetworkXError( "IntervalGraph: interval end must be bigger than or equal to begin: " "begin: {}, end: {}.".format(begin, end)) iedges = self.tree[begin:end] if multigraph: G = MultiDiGraph() else: G = DiGraph() if edge_data and edge_interval_data: G.add_edges_from((iedge.data[0], iedge.data[1], dict(self._adj[iedge.data[0]][iedge], begin=iedge.begin, end=iedge.end)) for iedge in iedges) elif edge_data: G.add_edges_from((iedge.data[0], iedge.data[1], self._adj[iedge.data[0]][iedge].copy()) for iedge in iedges) elif edge_interval_data: G.add_edges_from((iedge.data[0], iedge.data[1], { 'begin': iedge.begin, 'end': iedge.end }) for iedge in iedges) else: G.add_edges_from( (iedge.data[0], iedge.data[1]) for iedge in iedges) # include node attributes if node_data: G.add_nodes_from((n, self._node[n].copy()) for n in G.nodes) return G
class UtlExecutionSolver(ExecutionSolver): __slots__ = "__graph, __entryNode, __crashNode, __yesVectors, __allYes, __allNo"; """ @override __init__(): Process the graph, making our own CFG-only, massaged copy. @param G the graph """ def __init__(self, G): self.__graph = MultiDiGraph(); nodesToKeep = {n for n in G.nodes_iter(False) if is_cfg_node(G, n)}; self.__graph.add_nodes_from(nodesToKeep); # mark the entry node (self.__entryNode, isInterprocedural) = findGraphEntry(G); assert(self.__entryNode in self.__graph); # then, copy all CFG edges for n in self.__graph.nodes_iter(False): if(isInterprocedural and G.node[n].get("kind", "") == "call-site"): foundOne = False; for (source, target, attr) in G.out_edges_iter([n], data=True): if(attr.get("type", "flow") == "control" and \ attr.get("scope", "") == "interprocedural"): self.__graph.add_edge(n, target); foundOne = True; #end if #end for # add appropriate intraprocedural edges: only if # (a) the called function is not in the graphml, or # (b) the target is a crash point (which is essentially ambiguity # nonsensemeaning that we crashed trying to make the call itself) for (source, target, attr) in G.out_edges_iter([n], data=True): if(attr.get("type", "flow") == "flow" and \ attr.get("scope", "") != "interprocedural" and \ (not foundOne or G.node[target].get("kind", "") == "crash")): self.__graph.add_edge(n, target); #end if #end for elif(isInterprocedural and G.node[n].get("kind", "") == "exit"): entryForExit = findEntryForNode(G, n); for (source, target, attr) in G.in_edges_iter([entryForExit], data=True): if(attr.get("type", "flow") == "control" and \ attr.get("scope", "") == "interprocedural"): # edge from exit -> all successors of the call to this function for (call, callTarget, attr) in G.out_edges_iter([source], data=True): if(attr.get("type", "flow") == "flow" and \ attr.get("scope", "") != "interprocedural" and \ G.node[callTarget].get("kind", "") != "crash"): self.__graph.add_edge(n, callTarget); #end if #end for #end if #end for else: for (source, target, attr) in G.out_edges_iter([n], data=True): if(attr.get("type", "flow") == "flow" and \ attr.get("scope", "") != "interprocedural"): self.__graph.add_edge(n, target); #end if #end for #end if #end for # setup for yes, no, maybe, and crash constraints self.__crashNode = None; self.__yesVectors = set([]); self.__allYes = set([]); self.__allNo = set([]); #end: __init__ """ __findSCCFromNode(): Find the SCC in G that contains n. @param G the graph @param nodeToFind the node to find @return the node in G that contains n within its SCC (throws an exception if not found) """ def __findSCCFromNode(self, G, nodeToFind): for (n, data) in G.nodes_iter(data=True): if(nodeToFind in data.get("members", [])): return(n); #end for raise KeyError("Node " + str(nodeToFind) + " not found."); #end: __findSCCFromNode """ __isDAG(): Verify that the graph is a DAG (i.e., has no cycles). @param G the graph @return true if G is a DAG, otherwise false """ def __isDAG(self, G): # TODO: efficiency (this could be O(n)) for n in G.nodes_iter(data=False): visited = set([]); worklist = deque([target for (source, target) in G.out_edges_iter([n])]); while(worklist): current = worklist.popleft(); if(current == n): return(False); elif(current in visited): continue; else: visited.add(current); #end if for (source, target) in G.out_edges_iter([current]): worklist.append(target); #end for #end while #end for return(True); #end: __isDAG """ __clearNodeFacts(): Clear old before- and after-facts from all nodes in the SCC graph. @param G the graph NOTE: modifies G in-place """ def __clearNodeFacts(self, G): for (n, data) in G.nodes_iter(data=True): data["afterFact"] = None; data["beforeFact"] = None; #end for #end: __clearNodeFacts """ __isPrefix(): Check if the first list is a prefix of the second. @param first the first list @param second the second list @return true if first is a prefix of second, false otherwise """ def __isPrefix(self, first, second): if(len(second) < len(first)): return(False); #end if for (i, val) in enumerate(first): if(second[i] != val): return(False); #end if #end for return(True); #end: __isPrefix """ __getSCCEntryCrash(): Get the SCC for the entry and the crash node. It is an error if either is not found because G is not a SCC DAG. @param G the graph @return a tuple: (entrySCC, crashSCC) """ def __getSCCEntryCrash(self, G): sccEntry = self.__findSCCFromNode(G, self.__entryNode); sccCrash = self.__findSCCFromNode(G, self.__crashNode); return(sccEntry, sccCrash); #end: __getSCCEntryCrash """ __backwardReachableFrom(): Get all nodes in G backward-reachable from n. @param G the graph @param n the node to reach backward from @return the set of nodes (including n itself) """ def __backwardReachableFrom(self, G, n): reachable = set([]); worklist = [n]; while(worklist): current = worklist.pop(); if(current in reachable): continue; reachable.add(current); for (source, target) in G.in_edges_iter([current]): worklist.append(source); #end for #end while return(reachable); #end: __backwardReachableFrom """ __recReverseTopo(): A recursive sub-procedure for __reverseTopoOrdering(). @param G the graph, must be a DAG (i.e., SCC collapsed) @param n the node (from G) to recurse backward from @param ordering a deque that will hold the final ordering @param temporaryMark internal marking for "in-progress" nodes @param doneMark internal marking for completely processed nodes """ def __recReverseTopo(self, G, n, ordering, temporaryMark, doneMark): if(n in doneMark): return; elif(n in temporaryMark): print >> stderr, ("ERROR: graph for rev topo is not a DAG!"); exit(1); #end if temporaryMark.add(n); for (source, target) in G.in_edges_iter([n]): self.__recReverseTopo(G, source, ordering, temporaryMark, doneMark); #end for assert(n not in doneMark); doneMark.add(n); ordering.appendleft(n); #end: __recReverseTopo """ __reverseTopoOrdering(): Get a reverse topological ordering of the nodes in G, including only those nodes backward-reachable from crash. Note that G must be an acyclic graph. Further, this ordering may not include all of G's nodes, meaning that some nodes WON'T have all decendants processed before themselves...but they will only exclude those nodes that are not backward-reachable from crash. @param G the graph, must be a DAG (i.e., SCC collapsed) @param crash the ending point to use for the reverse ordering @return a reverse topological ordering of G's nodes, starting with crash. Crashes with error if G is not a DAG. """ def __reverseTopoOrdering(self, G, crash): ordering = deque(); temporaryMark = set([]); doneMark = set([]); self.__recReverseTopo(G, crash, ordering, temporaryMark, doneMark); return(ordering); #end: __reverseTopoOrdering """ __entryCrashPath(): Check if there exists a path in the DAG from the entry node's SCC, to the crash node's SCC. The path must also meet all the obsYes (__yesVectors) conditions. @param G the graph, must be a DAG (i.e., SCC collapsed) @return true if a consistent path entry->crash exists, otherwise false """ def __entryCrashPath(self, G): # an empty graph clearly has no path from entry->crash if(not G): return(False); #end if self.__clearNodeFacts(G); # get the SCC for the entry and the crash (realEntry, realCrash) = self.__getSCCEntryCrash(G); # get those nodes backward-reachable from the crash crashReachable = self.__backwardReachableFrom(G, realCrash); # get a reverse topological order of nodes in G (up to and including the # crash) revTopoOrdering = self.__reverseTopoOrdering(G, realCrash); assert(set(revTopoOrdering) == crashReachable); for processing in revTopoOrdering: # --------------------------------------------------------------------- # compute this node's "after-fact" from the union of all incoming nodes # (i.e., the incoming fact from children) # --------------------------------------------------------------------- afterFact = None; for (source, target) in G.out_edges_iter([processing]): childFact = G.node[target].get("beforeFact", None); if(childFact == None): assert(target not in crashReachable); continue; else: assert(len(childFact) == len(self.__yesVectors)); #end if if(afterFact == None): afterFact = childFact; continue; else: assert(len(childFact) == len(afterFact)); #end if # the hard case: in order to be a consistent path, one child must have # the smallest vector across all obsYes entries newIsSmaller = False; oldIsSmaller = False; for (i, obsYesVector) in enumerate(childFact): thisNewSmaller = self.__isPrefix(obsYesVector, afterFact[i]); thisOldSmaller = self.__isPrefix(afterFact[i], obsYesVector); assert(thisNewSmaller or thisOldSmaller); # update which is the smaller vector (more obsYes entries eaten) if(thisNewSmaller and thisOldSmaller): # they are the same: always OK continue; elif(thisNewSmaller): if(oldIsSmaller): # KABOOM! return(False); #end if newIsSmaller = True; else: if(newIsSmaller): # KABOOM! return(False); #end if oldIsSmaller = True; #end if #end for assert(not newIsSmaller or not oldIsSmaller); if(newIsSmaller): afterFact = childFact; #end if #end for # generate the "base" after-fact (for starting at the crash) if(afterFact == None): assert(processing == realCrash); afterFact = []; for obsYesVector in self.__yesVectors: # NOTE: don't need to do a deep copy here, because we will copy and # update the vector later (when computing the before-fact. Keep an # eye on this for future changes! afterFact += [list(obsYesVector)]; #end for #end if # --------------------------------------------------------------------- # compute this node's "before-fact" # --------------------------------------------------------------------- beforeFact = []; # build the new before-fact for vector in afterFact: # create our own copy of the fact, remove any nodes from our SCC from # the end of the vector newVector = vector[:]; while(newVector and newVector[-1] in G.node[processing]["members"]): newVector.pop(); #end while beforeFact.append(newVector); #end for # TODO: if any nodes from this SCC still in any vectors, KABOOM! # update the before-fact assert(G.node[processing].get("beforeFact", None) == None); G.node[processing]["beforeFact"] = beforeFact; #end for # check the fact at the entry entryBeforeFact = G.node[realEntry].get("beforeFact", None); if(entryBeforeFact == None): return(False); assert(len(entryBeforeFact) == len(self.__yesVectors)); for vector in entryBeforeFact: if(len(vector) > 0): return(False); #end if #end for return(True); #end: __entryCrashPath """ __removeDeadNodes(): Remove all nodes that are either (1) not forward-reachable from entry, or (2) not backward-reachable from the crash site @param G the graph (may be either a standard graph or an SCC DAG) NOTE: G is updated in-place """ def __removeDeadNodes(self, G): assert(self.__entryNode); assert(self.__crashNode); if(self.__entryNode in G and self.__crashNode in G): entryNode = self.__entryNode; crashNode = self.__crashNode; else: (entryNode, crashNode) = self.__getSCCEntryCrash(G); #end if fwdNodes = set([]); worklist = set([entryNode]); while(worklist): current = worklist.pop(); fwdNodes.add(current); for (source, target) in G.out_edges_iter([current]): if(target not in fwdNodes): worklist.add(target); #end for #end while bwdNodes = set([]); worklist = set([crashNode]); while(worklist): current = worklist.pop(); bwdNodes.add(current); for (source, target) in G.in_edges_iter([current]): if(source not in bwdNodes): worklist.add(source); #end for #end while oldNodes = set(G.nodes()); newNodes = oldNodes & fwdNodes & bwdNodes; G.remove_nodes_from(oldNodes - newNodes); #end: __removeDeadNodes """ __buildSCCGraph(): Build a new graph with SCCs from the input graph collapsed. NOTE: the created graph contains only SCCs backward-reachable from the crash node (which must be set before calling this function). @param G the graph @return the new SCC graph """ def __buildSCCGraph(self, G): newGraph = condensation(G); self.__removeDeadNodes(newGraph); return(newGraph); #end: __buildSCCGraph """ @override isSat(): Check if there is a path from entry to crash. @return whether or not any such path exists """ def isSat(self): assert(self.__entryNode != None); assert(self.__crashNode != None); return(self.__entryCrashPath(self.__buildSCCGraph(self.__graph))); #end: isSat """ @override encodeObsYes(): Encode the constraint for a yes-executed observation. @param possibleYes a set of possible matches to the true entry (NOTE: currently only supports a singleton) => [{G.nodes}] """ def encodeObsYes(self, possibleYes): thisEntry = []; for group in possibleYes: assert(len(group) == 1); n = next(iter(group)); self.__allYes.add(n); thisEntry.append(n); #end for self.__yesVectors.add(tuple(thisEntry)); #end: encodeObsYes """ @override encodeCrash(): Encode the constraint for the crashing location. @param crashStack a representation of possible crashes in the stack trace, ending in the final possible crashing nodes => [({G.nodes}, {G.nodes}), ..., ({G.nodes}, None)] """ def encodeCrash(self, crashStack): assert(self.__crashNode is None); obsCrash = []; for (callNodes, entryNodes) in crashStack: obsCrash.append(callNodes); if(entryNodes): obsCrash.append(entryNodes); #end for self.encodeObsYes(obsCrash); assert(len(crashStack[-1]) == 2); crashNodes = crashStack[-1][0]; assert(len(crashNodes) == 1); self.__crashNode = next(iter(crashNodes)); #end: encodeCrash """ @override encodeObsNo(): Encode the constraint for a not-executed observation. @param possibleNo a set of possible matches to the true entry (NOTE: currently only supports a singleton) => {G.nodes} """ def encodeObsNo(self, possibleNo): assert(len(possibleNo) == 1); # remove node from the graph n = next(iter(possibleNo)); self.__graph.remove_node(n); self.__allNo.add(n); # TODO: remove all now-disconnected nodes in the graph? # it's probably better to just do this once, after all "no" observations #end: encodeObsNo """ @override findKnownExecution(): Figure out which nodes in the CFG (a) are known to have executed at least once, (b) are known to have not executed, and (c) may or may not have executed given the crash location. @return (defYes, defNo, maybe) => ({G.nodes}, {G.nodes}, {G.nodes}) """ def findKnownExecution(self): defYes = set([]); defNo = self.__allNo.copy(); maybe = set([]); # build the base SCC graph (which is re-used for each exeNo check) baseSCCGraph = self.__buildSCCGraph(self.__graph); total = len(self.__graph); soFar = 0; for n in self.__graph.nodes_iter(False): prevInYesVectors = (tuple([n]) in self.__yesVectors); self.__yesVectors.add(tuple([n])); possibleYes = self.__entryCrashPath(baseSCCGraph); if(not prevInYesVectors): self.__yesVectors.remove(tuple([n])); #end if if(n in [self.__entryNode, self.__crashNode]): possibleNo = False; else: # make a shallow copy (so we can remove a node without wrecking the # original) noTestG = self.__graph.subgraph(self.__graph.nodes()); noTestG.remove_node(n); noTestSCCGraph = self.__buildSCCGraph(noTestG); possibleNo = self.__entryCrashPath(noTestSCCGraph); #end if if(not possibleYes and not possibleNo): print >> stderr, ("ERROR: graph node " + n + " neither executed nor " +\ "didn't execute!"); exit(1); elif(possibleYes and possibleNo): maybe.add(n); elif(possibleYes): defYes.add(n); else: defNo.add(n); soFar += 1; if(soFar % 10 == 0): stdout.write("\r" + ("%.2f" % ((1.0*soFar)/(1.0*total)*100)) + "%: " + \ str(soFar) + " / " + str(total)); stdout.flush(); #end if #end for print(""); return(defYes, defNo, maybe);
def to_subgraph(self, begin, end, inclusive=(True, False), multigraph=False, edge_data=False, edge_timestamp_data=False, node_data=False): """Return a networkx Graph or MultiGraph which includes all the nodes and edges which have timestamps within the given interval. Parameters ---------- begin: int or float end: int or float Must be bigger than or equal to begin. inclusive: 2-tuple boolean that determines inclusivity of begin and end multigraph: bool, optional (default= False) If True, a networkx MultiGraph will be returned. If False, networkx Graph. edge_data: bool, optional (default= False) If True, edges will keep their attributes. edge_timestamp_data: bool, optional (default= False) If True, each edge's attribute will also include its timestamp data. If `edge_data= True` and there already exist edge attributes named timestamp it will be overwritten. node_data : bool, optional (default= False) if True, each node's attributes will be included. See Also -------- to_snapshots : divide the impulse graph to snapshots Notes ----- If multigraph= False, and edge_data=True or edge_interval_data=True, in case there are multiple edges, only one will show with one of the edge's attributes. Note: nodes with no edges will not appear in any subgraph. Examples -------- >>> G = dnx.ImpulseGraph() >>> G.add_edges_from([(1, 2, 10), (2, 4, 11), (6, 4, 19), (2, 4, 15)]) >>> H = G.to_subgraph(4, 12) >>> type(H) <class 'networkx.classes.graph.DiGraph'> >>> list(H.edges(data=True)) [(1, 2, {}), (2, 4, {})] >>> H = G.to_subgraph(10, 12, edge_timestamp_data=True) >>> type(H) <class 'networkx.classes.graph.DiGraph'> >>> list(H.edges(data=True)) [(1, 2, {'timestamp': 10}), (2, 4, {'timestamp': 11})] >>> M = G.to_subgraph(4, 12, multigraph=True, edge_timestamp_data=True) >>> type(M) <class 'networkx.classes.multigraph.MultiDiGraph'> >>> list(M.edges(data=True)) [(1, 2, {'timestamp': 10}), (2, 4, {'timestamp': 11})] """ iedges = self.__search_tree(begin, end, inclusive=inclusive) if multigraph: G = MultiDiGraph() else: G = DiGraph() if edge_data and edge_timestamp_data: G.add_edges_from((iedge[0], iedge[1], dict(self._pred[iedge[0]][iedge[1]][iedge], timestamp=iedge[3])) for iedge in iedges) elif edge_data: G.add_edges_from( (iedge[0], iedge[1], self._pred[iedge[0]][iedge[1]][iedge]) for iedge in iedges) elif edge_timestamp_data: G.add_edges_from((iedge[0], iedge[1], { 'timestamp': iedge[3] }) for iedge in iedges) else: G.add_edges_from((iedge[0], iedge[1]) for iedge in iedges) if node_data: G.add_nodes_from((n, self._node[n].copy()) for n in G.nodes) return G