def examine_node_attributes(): ''' A node can contain arbitrary attributes - The <Graph>.node attribute is a regular Python dict whose key-value pairs are node identifiers and Python dicts - The documentation says that nodes cannot be added to the graph through <Graph>.node, but the code shows they can! - However, doing this can mess up the structure of the graph. The newly added node might not have its own Python dict if the update was done incorrectly - "Warning: adding a node to G.node does not add it to the graph." Listen to the documentation and never try to add nodes via <Graph>.node ''' g = Graph([(1, 5), (5, 5)]) print(g.nodes()) # [1, 5] print(g.edges()) # [(1, 5), (5, 5)] print(type(g.node)) # <class 'dict'> # Get the dict of an arbitrary node, but not the node itself (annoying) print(type(g.node[5])) # <class 'dict'> print(g.node[5]) # {} g.node[5]['pet'] = 'cat' print(g.node[5]) # {'pet': 'cat'} ## NEVER DO THIS # This is how to mess up the graph g.node[6] = 'psych' # The node exists, but are there underlying problems I'm not aware of? print(g.nodes()) # [1, 5, 6] # Here's the first problem. This should be a dict, but it's a str print(type(g.node[6])) # <class 'str'> # This error occurs because of the abuse of the API #g.add_node(6) # AttributeError: 'str' object has no attribute 'update' # This is how to "correctly" add a node via <Graph>.node, but never do this g.node[6] = {'psych': 'psych'} g.add_node(6) print(g.nodes()) # [1, 5, 6]
def add_node(): ''' - Adding a duplicate node does not add another node to the graph - Nodes must be unique. I cannot add two different nodes with the same value - Use the ability to add arbitrary attributes to nodes in order to make multiple nodes have the same "value" - From a technical standpoint, there is no reason why I can't have two nodes that are identified by the number 5. However, from a traveral perspective, it makes sense to require that nodes be unique. If I had two nodes whose primary identifier was 5, how would the code know which node I was interested in when I looked up 5? - What's annoying about this is that since nodes must be unique, then there's no reason why I can't look them up by key! - This restriction must be due to performance? - But this can't be! There IS already a dictionary that contains a reference to every node! It's <Graph>.node! So it's just poor design - Adding a duplicate node can update the aribtrary node attribute dict (see below) - A node can be added along with attributes ''' g = Graph([(1, 5), (5, 5)]) g.node[5]['foo'] = 'bar' # A duplicate node cannot remove existing arbitrary node attributes #g.add_node(5, {}) #print(g.node[5]) # {'foo': 'bar'} # A duplicate node can overwrite existing arbitrary node attributes #g.add_node(5, foo='bleh') #print(g.node[5]) # {'foo': 'bleh'} # A duplicate node can add new arbitrary node attributes #g.add_node(5, bless='you') #print(g.node[5]) # {'foo': 'bar', 'bless': 'you'} print(g.nodes()) # [1, 5] g.add_node(6, size='big', sound='quack') print(g.node[6]) # {'size': 'big', 'sound': 'quack'}
def add_node(self, n): if n in self: return # already in tree elif len(self.adj)==0: Graph.add_node(self,n) # first node else: # not allowed raise NetworkXError(\ "adding single node %s not allowed in non-empty tree"%(n))
def add_node_(): g = Graph() attributes = {'foo': 'bar'} # Wrong in v2.4 #g.add_node('9', attr_dict=attributes) # Right in v2.4 g.add_node('9', **attributes) print(g.nodes(data=True)) # [('9', {'foo': 'bar'})]
def add_node(self, n): if n in self: return # already in tree elif len(self.adj) == 0: Graph.add_node(self, n) # first node else: # not allowed raise NetworkXError(\ "adding single node %s not allowed in non-empty tree"%(n))
def create_graph_from_graph(): '''Passing another graph object appears to have the same net result as <Graph>.copy()''' g = Graph([(1, 5), (5, 5)]) g.add_node(6) h = Graph(g) print(h.nodes()) # [1, 5, 6] print(h.edges()) # [(1, 5), (5, 5)] g.node[1]['color'] = 'black' print(g.node[1]) # {'color': 'black'} print(h.node[1]) # {}
def examine_node_attributes(): ''' The v2.4 API is slightly different because it uses <Graph>.nodes (or <Graph>.nodes()) while <Graph>.node doesn't exist - Even though the NodeView is new, the underlying Python dicts that store attributes are the same as v1.11 ''' g = Graph([(1, 5), (5, 5)]) g.add_node(5) print(g.nodes()) # [1, 5] print(g.edges()) # [(1, 5), (5, 5)] #print(g.node[5]) # AttributeError: 'Graph' object has no attribute 'node' print(g.nodes[5]) # {} g.nodes[5]['foo'] = 'bar' print(g.nodes[5]) # {'foo': 'bar'}
def get_nodes(): ''' <Graph>.nodes() returns a new list that contains the unique identifiers that represent nodes in the graph - Modifying this list does nothing to the original graph - I can only access nodes by index, not key ''' g = Graph([(1, 5), (5, 5)]) g.add_node(5) print(type(g.nodes())) # <class 'list'> print(g.nodes()) # [1, 5] print(g.nodes()[0]) # 1 g.nodes().append(6) print(g.nodes()) # [1, 5]
def create_graph_from_edgeview(): ''' Fortunately, creating a graph from the EdgeView of another graph does not perform a shallow copy of the original graph - The new graph gets entirely new edges - Arbitrary attributes of the old edges are not copied - This approach will miss copying nodes who had no edges ''' g = Graph([(1, 'foo'), (1, 3)]) g.add_node(4) h = Graph(g.edges) g.edges[1, 'foo']['key'] = 'value' print(h.nodes) # [1, 'foo', 3] print(h.edges) # [(1, 'foo'), (1, 3)] print(h.edges[1, 'foo']) # {} print(g.edges[1, 'foo']) # {'key': 'value'}
def create_graph_with_new_data(): ''' - Parallel edges are not allowed - The data can be: - An edge list - Any NetworkX graph object - The data cannot be a flat list of nodes! - It can't be a list of nodes because a bunch of unconnected nodes don't compose a very interesting graph - Nodes can be any hashable object. Recall that immutable objects are implicitly hashable: int, string, tuple, etc. ''' # Create with edge list. Since this is an undirected graph, there is only one edge between 1 and 3 g = Graph([(1, 'foo'), (1, 3), (3, 1)]) g.add_node(6) # Create with another graph print(g.nodes()) # [1, 'foo', 3, 6] print(g.edges()) # [(1, 'foo'), (1, 3)]
def make_nonmultigraph(multigraph): """ Removes duplicate edges. Instead of having multiple edges going from the same source to the same target, this function adds one edge with a weight attribute, Parameters: multigraph: The multi-graph with multi-edges Return: G: A new graph which is equivalent to the multi-graph. """ G = Graph() for node in multigraph.nodes_iter(): G.add_node(node) for edge in multigraph.edges_iter(): for existing_edge in G.edges_iter(): if existing_edge[0] == edge[0] and existing_edge[1] == edge[1]: #If the edge is already in the existing edge list... G.edge[edge[0]][edge[1]]['weight'] += 1 # the existing edge's weight is incremented G.add_edge(edge[0], edge[1], weight=1) return G
def get_nodes_and_data(): ''' The <Graph>.nodes() method accepts an optional kwarg "data" that, regardless of value, is interpreted as a truthy or falsy value - If truthy, a list of 2-tuples is returned. Each 2-tuple consists of (<key>, <attr dict>) - If falsy, a list of keys is returned In v1.11 that's all that happens. It's very simple ''' g = Graph() g.add_nodes_from(['a', 'b'], data='quack') g.add_node('c', data=False) g.add_node('d') print(g.nodes()) # ['a', 'b', 'c', 'd'] print(g.nodes(data=False)) # ['a', 'b', 'c', 'd'] print( g.nodes(data=True) ) # [('a', {'data': 'quack'}), ('b', {'data': 'quack'}), ('c', {'data': False}), ('d', {})] print( g.nodes(data='no') ) # [('a', {'data': 'quack'}), ('b', {'data': 'quack'}), ('c', {'data': False}), ('d', {})]
def create_graph_from_edges(): ''' - Don't use this strategy alone to copy a graph - None of the arbitrary attributes from nodes, edges, or the graph are copied - When a new graph is created from the edges of an existing graph, the nodes that form each edge are also created - Thus, creating a copy from edges is almost a true copy - It is NOT a true copy because any nodes without edges are not copied! ''' g = Graph([(1, 5), (5, 5)]) g.add_node(6) g.edge[1][5]['foo'] = 'bar' print(g.edge[1][5]) # {'foo': 'bar'} print(g.nodes()) # [1, 5, 6] h = Graph() # Yes, this is the required syntax to do this h.add_edges_from(g.edges()) print(h.edge[1][5]) # {} print(h.nodes()) # [1, 5] print(h.edges()) # [(1, 5), (5, 5)]
def create_true_graph_copy(): ''' - <Graph>.copy() returns a true copy of the original graph - == operator for graph objects is useless, or perhaps __eq__ isn't implemented so it falls back to "is" behavior - This is NOT what I want if I don't want to copy attributes ''' g = Graph([(1, 5), (5, 5)]) g.add_node(6) g.graph['pie'] = 'apple' g.node[1]['pet'] = 'fish' g.edge[5][5]['sound'] = 'clink' h = g.copy() print(h.nodes()) # [1, 5, 6] print(h.edges()) # [(1, 5), (5, 5)] print(h.graph) # {'pie': 'apple'} print(h.node) # {1: {'pet': 'fish'}, 5: {}, 6: {}} print(h.edge) # {1: {5: {}}, 5: {1: {}, 5: {'sound': 'clink'}}, 6: {}} print(h is g) # False print(h == g) # False
def create_graph_from_nodes(): ''' The net result is the same as in v1.11: the new graph gets all new nodes that are copies of the original nodes - No edges are copied - No arbitrary attributes are copied ''' g = Graph([(1, 5), (5, 5)]) g.graph['baz'] = 'boo' g.add_node(6) print(g.nodes()) # [1, 5, 6] print(g.edges()) # [(1, 5), (5, 5)] g.nodes[5]['foo'] = 'bar' print(g.nodes[5]) # {'foo': 'bar'} h = Graph() h.add_nodes_from(g) print(h.graph) # {} print(h.nodes()) # [1, 5, 6] print(h.edges()) # {} print(h.nodes[5]) # {}
def create_graph_from_nodes(): ''' - Don't use this strategy alone to copy a graph - None of the arbitrary attributes from nodes, edges, or the graph are copied - If the nodes of a graph object are used to create a new graph object: - No edges are created in the new graph. ''' g = Graph([(1, 5), (5, 5)]) g.graph['baz'] = 'boo' g.add_node(6) print(g.nodes()) # [1, 5, 6] print(g.edges()) # [(1, 5), (5, 5)] g.node[5]['foo'] = 'bar' print(g.node[5]) # {'foo': 'bar'} h = Graph() h.add_nodes_from(g) print(h.graph) # {} print(h.nodes()) # [1, 5, 6] print(h.edges()) # {} print(h.node[5]) # {}
def make_nonmultigraph(multigraph): """ Removes duplicate edges. Instead of having multiple edges going from the same source to the same target, this function adds one edge with a weight attribute, Parameters: multigraph: The multi-graph with multi-edges Return: G: A new graph which is equivalent to the multi-graph. """ G = Graph() for node in multigraph.nodes_iter(): G.add_node(node) for edge in multigraph.edges_iter(): for existing_edge in G.edges_iter(): if existing_edge[0] == edge[0] and existing_edge[1] == edge[ 1]: #If the edge is already in the existing edge list... G.edge[edge[0]][edge[1]][ 'weight'] += 1 # the existing edge's weight is incremented G.add_edge(edge[0], edge[1], weight=1) return G
def get_nodes_and_data(): ''' <Graph>.nodes() is completely different in v2.4 - The "data" kwarg can do three things: - If data==True, return all node keys and all their data - If data==False, return just a list of node keys - If data==<attribute key>, return all node keys with the attribute key, along with the value of the attribute - In combination with "data", the "default" kwarg will substitute <value> for every node that doesn't have the searched-for attribute key ''' g = Graph() g.add_nodes_from(['a', 'b'], data='quack', size='tiny') g.add_node('c', data=False) g.add_node('d') print(g.nodes()) # ['a', 'b', 'c', 'd'] # Get no node attribute values print(g.nodes(data=False)) # ['a', 'b', 'c', 'd'] # Get all node attribute key-value pairs print(g.nodes(data=True)) # [('a', {'data': 'quack', 'size': 'tiny'}), ('b', {'data': 'quack', 'size': 'tiny'}), ('c', {'data': False}), ('d', {})] # Get all node keys and attribute values for the given attribute key print(g.nodes(data='size')) # [('a', 'tiny'), ('b', 'tiny'), ('c', None), ('d', None)] # Substitute a value for nodes that don't have the key print(g.nodes(data='size', default='purple')) # [('a', 'tiny'), ('b', 'tiny'), ('c', 'purple'), ('d', 'purple')]
def _check_cycle(mode_declarations): g = Graph().to_directed() for decl in mode_declarations: input_args = decl.input_arguments() output_args = decl.output_arguments() for o in output_args: g.add_node(o.name) for i in input_args: g.add_node(i.name) for o in output_args: g.add_edge(i.name, o.name) try: res = find_cycle(g) except NetworkXNoCycle: return raise CheckFailed('Cycle found: %s' % str(res))
class RouterBase(Routable): def __init__(self): super().__init__() self._table = {self.url: self.url} self._graph = Graph() def addItem(self, dst_url): pass def removeNeighbor(self, downs): for url in downs: if url in self._table: del self._table[url] def getNextHop(self, dst_url): downs = [] for url in self.neighbors: try: s = self.getProxy(url) s.testProxy(0) except OSError: downs.append(url) if downs: self.removeNeighbor(downs) if dst_url not in self._table: self.addItem(dst_url) try: return self._table[dst_url] except KeyError: return "" @property def neighbors(self): if self.url not in self._graph: self._graph.add_node(self.url) return self._graph[self.url]
def add_node(self, n): Graph.add_node(self, n) # this is not called from add_edge so we must assign # and component (else that is assigned in add_edge) self.comp[n] = self.nc self.nc += 1
def add_node(self, n): Graph.add_node(self,n) # this is not called from add_edge so we must assign # and component (else that is assigned in add_edge) self.comp[n]=self.nc self.nc+=1