def read(string): """ Read a graph from a string in Dot language and return it. Nodes and edges specified in the input will be added to the current graph. @type string: string @param string: Input string in Dot format specifying a graph. @rtype: graph @return: Graph """ dotG = pydot.graph_from_dot_data(string) if (dotG.get_type() == "graph"): G = graph() elif (dotG.get_type() == "digraph"): G = digraph() elif (dotG.get_type() == "hypergraph"): return read_hypergraph(string) else: raise InvalidGraphType # Read nodes... # Note: If the nodes aren't explicitly listed, they need to be for each_node in dotG.get_nodes(): G.add_node(each_node.get_name()) for each_attr_key, each_attr_val in each_node.get_attributes().items(): G.add_node_attribute(each_node.get_name(), (each_attr_key, each_attr_val)) # Read edges... for each_edge in dotG.get_edges(): # Check if the nodes have been added if not G.has_node(each_edge.get_source()): G.add_node(each_edge.get_source()) if not G.has_node(each_edge.get_destination()): G.add_node(each_edge.get_destination()) # See if there's a weight if 'weight' in each_edge.get_attributes().keys(): _wt = each_edge.get_attributes()['weight'] else: _wt = 1 # See if there is a label if 'label' in each_edge.get_attributes().keys(): _label = each_edge.get_attributes()['label'] else: _label = '' G.add_edge((each_edge.get_source(), each_edge.get_destination()), wt = _wt, label = _label) for each_attr_key, each_attr_val in each_edge.get_attributes().items(): if not each_attr_key in ['weight', 'label']: G.add_edge_attribute((each_edge.get_source(), each_edge.get_destination()), \ (each_attr_key, each_attr_val)) return G
def read_graph_from_string(txt): """Read a graph from a string, either in dot format, or our own compressed format. Returns: `pygraph.digraph`: Graph object. """ if not txt.startswith('{'): return read_dot(txt) # standard dot format def conv(value): if isinstance(value, basestring): return '"' + value + '"' else: return value # our compacted format doc = literal_eval(txt) g = digraph() for attrs, values in doc.get("nodes", []): attrs = [(k, conv(v)) for k, v in attrs] for value in values: if isinstance(value, basestring): node_name = value attrs_ = attrs else: node_name, label = value attrs_ = attrs + [("label", conv(label))] g.add_node(node_name, attrs=attrs_) for attrs, values in doc.get("edges", []): attrs_ = [(k, conv(v)) for k, v in attrs] for value in values: if len(value) == 3: edge = value[:2] label = value[-1] else: edge = value label = '' g.add_edge(edge, label=label, attrs=attrs_) return g
def generate(num_nodes, num_edges, directed=False, weight_range=(1, 1)): """ Create a random graph. @type num_nodes: number @param num_nodes: Number of nodes. @type num_edges: number @param num_edges: Number of edges. @type directed: bool @param directed: Whether the generated graph should be directed or not. @type weight_range: tuple @param weight_range: tuple of two integers as lower and upper limits on randomly generated weights (uniform distribution). """ # Graph creation if directed: random_graph = digraph() else: random_graph = graph() # Nodes nodes = range(num_nodes) random_graph.add_nodes(nodes) # Build a list of all possible edges edges = [] edges_append = edges.append for x in nodes: for y in nodes: if ((directed and x != y) or (x > y)): edges_append((x, y)) # Randomize the list shuffle(edges) # Add edges to the graph min_wt = min(weight_range) max_wt = max(weight_range) for i in range(num_edges): each = edges[i] random_graph.add_edge((each[0], each[1]), wt = randint(min_wt, max_wt)) return random_graph
def read(string): """ Read a graph from a XML document and return it. Nodes and edges specified in the input will be added to the current graph. @type string: string @param string: Input string in XML format specifying a graph. @rtype: graph @return: Graph """ dom = parseString(string) if dom.getElementsByTagName("graph"): G = graph() elif dom.getElementsByTagName("digraph"): G = digraph() elif dom.getElementsByTagName("hypergraph"): return read_hypergraph(string) else: raise InvalidGraphType # Read nodes... for each_node in dom.getElementsByTagName("node"): G.add_node(each_node.getAttribute('id')) for each_attr in each_node.getElementsByTagName("attribute"): G.add_node_attribute(each_node.getAttribute('id'), (each_attr.getAttribute('attr'), each_attr.getAttribute('value'))) # Read edges... for each_edge in dom.getElementsByTagName("edge"): if (not G.has_edge( (each_edge.getAttribute('from'), each_edge.getAttribute('to')))): G.add_edge((each_edge.getAttribute('from'), each_edge.getAttribute('to')), \ wt = float(each_edge.getAttribute('wt')), label = each_edge.getAttribute('label')) for each_attr in each_edge.getElementsByTagName("attribute"): attr_tuple = (each_attr.getAttribute('attr'), each_attr.getAttribute('value')) if (attr_tuple not in G.edge_attributes((each_edge.getAttribute('from'), \ each_edge.getAttribute('to')))): G.add_edge_attribute((each_edge.getAttribute('from'), \ each_edge.getAttribute('to')), attr_tuple) return G
def read(string): """ Read a graph from a string in Dot language and return it. Nodes and edges specified in the input will be added to the current graph. @type string: string @param string: Input string in Dot format specifying a graph. @rtype: graph @return: Graph """ dotG = pydot.graph_from_dot_data(string) # This is awful, however there seems to be a major incompatibility with pygraph # and current pydot. Pydot now returns a list of graphs from a dot string. Rather # than possibly rewrite a big chunk of this lib, we'll just use the first graph. # Since rez only makes single graphs anyway, this should suffice. # # https://github.com/nerdvegas/rez/issues/884 # # <hack> if isinstance(dotG, list): dotG = dotG[0] # </endhack> if (dotG.get_type() == "graph"): G = graph() elif (dotG.get_type() == "digraph"): G = digraph() elif (dotG.get_type() == "hypergraph"): return read_hypergraph(string) else: raise InvalidGraphType # Read nodes... # Note: If the nodes aren't explicitly listed, they need to be for each_node in dotG.get_nodes(): G.add_node(each_node.get_name()) for each_attr_key, each_attr_val in each_node.get_attributes().items(): G.add_node_attribute(each_node.get_name(), (each_attr_key, each_attr_val)) # Read edges... for each_edge in dotG.get_edges(): # Check if the nodes have been added if not G.has_node(each_edge.get_source()): G.add_node(each_edge.get_source()) if not G.has_node(each_edge.get_destination()): G.add_node(each_edge.get_destination()) # See if there's a weight if 'weight' in each_edge.get_attributes().keys(): _wt = each_edge.get_attributes()['weight'] else: _wt = 1 # See if there is a label if 'label' in each_edge.get_attributes().keys(): _label = each_edge.get_attributes()['label'] else: _label = '' G.add_edge((each_edge.get_source(), each_edge.get_destination()), wt=_wt, label=_label) for each_attr_key, each_attr_val in each_edge.get_attributes().items(): if not each_attr_key in ['weight', 'label']: G.add_edge_attribute((each_edge.get_source(), each_edge.get_destination()), \ (each_attr_key, each_attr_val)) return G
def get_reverse_dependency_tree(package_name, depth=None, paths=None, build_requires=False, private_build_requires=False): """Find packages that depend on the given package. This is a reverse dependency lookup. A tree is constructed, showing what packages depend on the given package, with an optional depth limit. A resolve does not occur. Only the latest version of each package is used, and requirements from all variants of that package are used. Args: package_name (str): Name of the package depended on. depth (int): Tree depth limit, unlimited if None. paths (list of str): paths to search for packages, defaults to `config.packages_path`. build_requires (bool): If True, includes packages' build_requires. private_build_requires (bool): If True, include `package_name`'s private_build_requires. Returns: A 2-tuple: - (list of list of str): Lists of package names, where each list is a single depth in the tree. The first list is always [`package_name`]. - `pygraph.digraph` object, where nodes are package names, and `package_name` is always the leaf node. """ pkgs_list = [[package_name]] g = digraph() g.add_node(package_name) # build reverse lookup it = iter_package_families(paths) package_names = set(x.name for x in it) if package_name not in package_names: raise PackageFamilyNotFoundError("No such package family %r" % package_name) if depth == 0: return pkgs_list, g bar = ProgressBar("Searching", len(package_names)) lookup = defaultdict(set) for i, package_name_ in enumerate(package_names): it = iter_packages(name=package_name_, paths=paths) packages = list(it) if not packages: continue pkg = max(packages, key=lambda x: x.version) requires = [] for variant in pkg.iter_variants(): pbr = (private_build_requires and pkg.name == package_name) requires += variant.get_requires(build_requires=build_requires, private_build_requires=pbr) for req in requires: if not req.conflict: lookup[req.name].add(package_name_) bar.next() bar.finish() # perform traversal n = 0 consumed = set([package_name]) working_set = set([package_name]) node_color = "#F6F6F6" node_fontsize = 10 node_attrs = [("fillcolor", node_color), ("style", "filled"), ("fontsize", node_fontsize)] while working_set and (depth is None or n < depth): working_set_ = set() for child in working_set: parents = lookup[child] - consumed working_set_.update(parents) consumed.update(parents) for parent in parents: g.add_node(parent, attrs=node_attrs) g.add_edge((parent, child)) if working_set_: pkgs_list.append(sorted(list(working_set_))) working_set = working_set_ n += 1 return pkgs_list, g
def get_reverse_dependency_tree(package_name, depth=None, paths=None): """Find packages that depend on the given package. This is a reverse dependency lookup. A tree is constructed, showing what packages depend on the given package, with an optional depth limit. A resolve does not occur. Only the latest version of each package is used, and requirements from all variants of that package are used. Args: package_name (str): Name of the package depended on. depth (int): Tree depth limit, unlimited if None. paths (list of str): paths to search for packages, defaults to `config.packages_path`. Returns: A 2-tuple: - (list of list of str): Lists of package names, where each list is a single depth in the tree. The first list is always [`package_name`]. - `pygraph.digraph` object, where nodes are package names, and `package_name` is always the leaf node. """ pkgs_list = [[package_name]] g = digraph() g.add_node(package_name) # build reverse lookup it = iter_package_families(paths) package_names = set(x.name for x in it) if package_name not in package_names: raise PackageFamilyNotFoundError("No such package family %r" % package_name) if depth == 0: return pkgs_list, g bar = ProgressBar("Searching", len(package_names)) lookup = defaultdict(set) for i, package_name_ in enumerate(package_names): bar.next() it = iter_packages(name=package_name_, paths=paths) packages = list(it) if not packages: continue pkg = max(packages, key=lambda x: x.version) requires = set(pkg.requires or []) for req_list in pkg.variants or []: requires.update(req_list) for req in requires: if not req.conflict: lookup[req.name].add(package_name_) # perform traversal bar.finish() n = 0 consumed = set([package_name]) working_set = set([package_name]) node_color = "#F6F6F6" node_fontsize = 10 node_attrs = [("fillcolor", node_color), ("style", "filled"), ("fontsize", node_fontsize)] while working_set and (depth is None or n < depth): working_set_ = set() for child in working_set: parents = lookup[child] - consumed working_set_.update(parents) consumed.update(parents) for parent in parents: g.add_node(parent, attrs=node_attrs) g.add_edge((parent, child)) if working_set_: pkgs_list.append(sorted(list(working_set_))) working_set = working_set_ n += 1 return pkgs_list, g
def cut_tree(igraph, caps = None): """ Construct a Gomory-Hu cut tree by applying the algorithm of Gusfield. @type igraph: graph @param igraph: Graph @type caps: dictionary @param caps: Dictionary specifying a maximum capacity for each edge. If not given, the weight of the edge will be used as its capacity. Otherwise, for each edge (a,b), caps[(a,b)] should be given. @rtype: dictionary @return: Gomory-Hu cut tree as a dictionary, where each edge is associated with its weight """ #maximum flow needs a digraph, we get a graph #I think this conversion relies on implementation details outside the api and may break in the future graph = digraph() graph.add_graph(igraph) #handle optional argument if not caps: caps = {} for edge in graph.edges(): caps[edge] = igraph.edge_weight(edge) #temporary flow variable f = {} #we use a numbering of the nodes for easier handling n = {} N = 0 for node in graph.nodes(): n[N] = node N = N + 1 #predecessor function p = {}.fromkeys(range(N),0) p[0] = None for s in range(1,N): t = p[s] S = [] #max flow calculation (flow,cut) = maximum_flow(graph,n[s],n[t],caps) for i in range(N): if cut[n[i]] == 0: S.append(i) value = cut_value(graph,flow,cut) f[s] = value for i in range(N): if i == s: continue if i in S and p[i] == t: p[i] = s if p[t] in S: p[s] = p[t] p[t] = s f[s] = f[t] f[t] = value #cut tree is a dictionary, where each edge is associated with its weight b = {} for i in range(1,N): b[(n[i],n[p[i]])] = f[i] return b
def cut_tree(igraph, caps=None): """ Construct a Gomory-Hu cut tree by applying the algorithm of Gusfield. @type igraph: graph @param igraph: Graph @type caps: dictionary @param caps: Dictionary specifying a maximum capacity for each edge. If not given, the weight of the edge will be used as its capacity. Otherwise, for each edge (a,b), caps[(a,b)] should be given. @rtype: dictionary @return: Gomory-Hu cut tree as a dictionary, where each edge is associated with its weight """ #maximum flow needs a digraph, we get a graph #I think this conversion relies on implementation details outside the api and may break in the future graph = digraph() graph.add_graph(igraph) #handle optional argument if not caps: caps = {} for edge in graph.edges(): caps[edge] = igraph.edge_weight(edge) #temporary flow variable f = {} #we use a numbering of the nodes for easier handling n = {} N = 0 for node in graph.nodes(): n[N] = node N = N + 1 #predecessor function p = {}.fromkeys(range(N), 0) p[0] = None for s in range(1, N): t = p[s] S = [] #max flow calculation (flow, cut) = maximum_flow(graph, n[s], n[t], caps) for i in range(N): if cut[n[i]] == 0: S.append(i) value = cut_value(graph, flow, cut) f[s] = value for i in range(N): if i == s: continue if i in S and p[i] == t: p[i] = s if p[t] in S: p[s] = p[t] p[t] = s f[s] = f[t] f[t] = value #cut tree is a dictionary, where each edge is associated with its weight b = {} for i in range(1, N): b[(n[i], n[p[i]])] = f[i] return b
def read(string): """ Read a graph from a string in Dot language and return it. Nodes and edges specified in the input will be added to the current graph. @type string: string @param string: Input string in Dot format specifying a graph. @rtype: graph @return: Graph """ dotG = pydot.graph_from_dot_data(string) if (dotG.get_type() == "graph"): G = graph() elif (dotG.get_type() == "digraph"): G = digraph() elif (dotG.get_type() == "hypergraph"): return read_hypergraph(string) else: raise InvalidGraphType # Read nodes... # Note: If the nodes aren't explicitly listed, they need to be for each_node in dotG.get_nodes(): G.add_node(each_node.get_name()) for each_attr_key, each_attr_val in each_node.get_attributes().items(): G.add_node_attribute(each_node.get_name(), (each_attr_key, each_attr_val)) # Read edges... for each_edge in dotG.get_edges(): # Check if the nodes have been added if not G.has_node(each_edge.get_source()): G.add_node(each_edge.get_source()) if not G.has_node(each_edge.get_destination()): G.add_node(each_edge.get_destination()) # See if there's a weight if 'weight' in each_edge.get_attributes().keys(): _wt = each_edge.get_attributes()['weight'] else: _wt = 1 # See if there is a label if 'label' in each_edge.get_attributes().keys(): _label = each_edge.get_attributes()['label'] else: _label = '' G.add_edge((each_edge.get_source(), each_edge.get_destination()), wt=_wt, label=_label) for each_attr_key, each_attr_val in each_edge.get_attributes().items(): if not each_attr_key in ['weight', 'label']: G.add_edge_attribute((each_edge.get_source(), each_edge.get_destination()), \ (each_attr_key, each_attr_val)) return G