~ubuntu-branches/ubuntu/trusty/python-networkx/trusty-proposed

« back to all changes in this revision

Viewing changes to networkx/operators.py

  • Committer: Bazaar Package Importer
  • Author(s): Cyril Brulebois
  • Date: 2009-02-28 13:36:24 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20090228133624-9l5rwi1ftlzc7b0l
* Upload to unstable now that lenny is released (yay).
* Fix FTBFS with python-support 0.90.3: no longer rely on its internal
  behaviour, and xnow set tests/test.py executable right after “setup.py
  install” (Closes: #517065).
* Drop executable bits from bz2 files.
* Update Vcs-* fields: move from DPMT's svn to collab-maint's git.
* Remote DPMT from Uploaders, following Piotr Ożarowski's request.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
 
4
4
"""
5
5
__author__ = """Aric Hagberg (hagberg@lanl.gov)\nPieter Swart (swart@lanl.gov)\nDan Schult(dschult@colgate.edu)"""
6
 
__date__ = "$Date: 2007-07-18 15:23:23 -0600 (Wed, 18 Jul 2007) $"
7
 
__credits__ = """"""
8
 
__revision__ = "$Revision: 1024 $"
9
 
#    Copyright (C) 2004-2006 by 
 
6
#    Copyright (C) 2004-2008 by 
10
7
#    Aric Hagberg <hagberg@lanl.gov>
11
8
#    Dan Schult <dschult@colgate.edu>
12
9
#    Pieter Swart <swart@lanl.gov>
13
10
#    Distributed under the terms of the GNU Lesser General Public License
14
11
#    http://www.gnu.org/copyleft/lesser.html
15
12
 
 
13
__all__ = ['union', 'cartesian_product', 'compose', 'complement',
 
14
           'disjoint_union', 'create_empty_copy', 'subgraph', 
 
15
           'convert_node_labels_to_integers', 'relabel_nodes']
 
16
 
16
17
import networkx
17
18
from networkx.utils import is_string_like
18
19
 
19
 
def subgraph(G, nbunch, inplace=False, create_using=None):
 
20
def subgraph(G, nbunch, copy=True):
20
21
    """
21
22
    Return the subgraph induced on nodes in nbunch.
22
23
 
25
26
    of nodes. (It can be an iterable or an iterator, e.g. a list,
26
27
    set, graph, file, numeric array, etc.)
27
28
 
28
 
    Setting inplace=True will return the induced subgraph in the
 
29
    Setting copy=False will return the induced subgraph in the
29
30
    original graph by deleting nodes not in nbunch. This overrides
30
31
    create_using.  Warning: this can destroy the graph.
31
32
 
32
 
    Unless otherwise specified, return a new graph of the same
33
 
    type as self.  Use (optional) create_using=R to return the
34
 
    resulting subgraph in R. R can be an existing graph-like
35
 
    object (to be emptied) or R is a call to a graph object,
36
 
    e.g. create_using=DiGraph(). See documentation for
37
 
    empty_graph.
38
 
 
39
 
    Implemented for Graph, DiGraph, XGraph, XDiGraph
40
 
 
41
33
    Note: subgraph(G) calls G.subgraph()
42
34
 
43
35
    """
44
 
    H = G.subgraph(nbunch, inplace, create_using)
45
 
    return H
 
36
    return G.subgraph(nbunch, copy)
46
37
                                                                                
47
38
def union(G,H,create_using=None,rename=False,name=None):
48
39
    """ Return the union of graphs G and H.
62
53
    both directed or both undirected.
63
54
 
64
55
    A new name can be specified in the form
65
 
    X=graph_union(G,H,name="new_name")
 
56
    X=union(G,H,name="new_name")
66
57
 
67
58
    Implemented for Graph, DiGraph, XGraph, XDiGraph.        
68
59
 
70
61
    if name is None:
71
62
        name="union( %s, %s )"%(G.name,H.name)
72
63
    if create_using is None:
73
 
        R=create_empty_copy(G,with_nodes=False)
 
64
        R=G.__class__()
74
65
    else:
75
66
        R=create_using
76
67
        R.clear()
80
71
    # note that for objects w/o succinct __name__,
81
72
    # the new labels can be quite verbose
82
73
    # See also disjoint_union
83
 
    Gname={}
84
 
    Hname={}
85
74
    if rename: # create new string labels
 
75
        Gname={}
 
76
        Hname={}
86
77
        for v in G:
87
78
            if is_string_like(v):
88
 
                Gname.setdefault(v,rename[0]+v)
 
79
                Gname[v]=rename[0]+v
89
80
            else:
90
 
                Gname.setdefault(v,rename[0]+repr(v))
 
81
                Gname[v]=rename[0]+repr(v)
91
82
        for v in H:
92
83
            if is_string_like(v):
93
 
                Hname.setdefault(v,rename[1]+v)
 
84
                Hname[v]=rename[1]+v
94
85
            else:
95
 
                Hname.setdefault(v,rename[1]+repr(v))
 
86
                Hname[v]=rename[1]+repr(v)
96
87
    else:
97
 
        for v in G:
98
 
            Gname.setdefault(v,v)
99
 
        for v in H:
100
 
            Hname.setdefault(v,v)
 
88
        Gname=dict( ((v,v) for v in G) )
 
89
        Hname=dict( ((v,v) for v in H) )
101
90
 
102
91
    # node name check
103
 
    for n in Gname.values():
104
 
        if n in Hname.values():
 
92
    Hnames=set(Hname.values())
 
93
    Gnames=set(Gname.values())
 
94
    if Gnames & Hnames:
105
95
            raise networkx.NetworkXError,\
106
96
            "node sets of G and H are not disjoint. Use appropriate rename=('Gprefix','Hprefix')"
107
97
    # node names OK, now build union
108
 
    for v in G:
109
 
        R.add_node(Gname[v])
110
 
    G_edges=G.edges_iter()
111
 
    for e in G_edges:
112
 
        if len(e)==2:
113
 
            u,v=e
114
 
            R.add_edge(Gname[u],Gname[v])
115
 
        else:
116
 
            u,v,x=e
117
 
            R.add_edge(Gname[u],Gname[v],x)
118
 
    for v in H:
119
 
        R.add_node(Hname[v])
120
 
    H_edges=H.edges_iter()
121
 
    for e in H_edges:
122
 
        if len(e)==2:
123
 
            u,v=e
124
 
            R.add_edge(Hname[u],Hname[v])
125
 
        else:
126
 
            u,v,x=e
127
 
            R.add_edge(Hname[u],Hname[v],x)        
 
98
    R.add_nodes_from(Gnames)
 
99
    R.add_edges_from( ( (Gname[u],Gname[v],x) for u,v,x in G.edges_iter(data=True) ))
 
100
    R.add_nodes_from(Hnames)
 
101
    R.add_edges_from( ( (Hname[u],Hname[v],x) for u,v,x in H.edges_iter(data=True) ))
128
102
    return R
129
103
 
130
104
 
160
134
        for w in H:
161
135
            Prod.add_node((v,w)) 
162
136
 
163
 
    H_edges=H.edges_iter()
164
 
    for (w1,w2) in H_edges:
165
 
        for v in G:
166
 
            Prod.add_edge((v,w1),(v,w2))
167
 
 
168
 
    G_edges=G.edges_iter()
169
 
    for (v1,v2) in G_edges:
170
 
        for w in H:
171
 
            Prod.add_edge((v1,w),(v2,w))
 
137
    Prod.add_edges_from( (((v,w1),(v,w2),d) for w1,w2,d in H.edges_iter(data=True) for v in G) )
 
138
    Prod.add_edges_from( (((v1,w),(v2,w),d) for v1,v2,d in G.edges_iter(data=True) for w in H) )
172
139
 
173
140
    Prod.name="Cartesian Product("+G.name+","+H.name+")"
174
141
    return Prod
177
144
def compose(G,H,create_using=None, name=None):
178
145
    """ Return a new graph of G composed with H.
179
146
    
 
147
    Composition is the simple union of the node sets and edge sets.
180
148
    The node sets of G and H need not be disjoint.
181
149
 
182
150
    A new graph is returned, of the same class as G.
194
162
    if name is None:
195
163
        name="compose( %s, %s )"%(G.name,H.name)
196
164
    if create_using is None:
197
 
        R=create_empty_copy(G,with_nodes=False)
 
165
        R=G.__class__()
198
166
    else:
199
167
        R=create_using
200
168
        R.clear()
201
169
    R.name=name
202
 
    R.add_nodes_from([v for v in G.nodes() ])
203
 
    R.add_edges_from(G.edges())
204
 
    R.add_nodes_from([v for v in H.nodes() ])
205
 
    R.add_edges_from(H.edges())
 
170
    R.add_nodes_from(G)
 
171
    R.add_edges_from(G.edges_iter(data=True))
 
172
    R.add_nodes_from(H)
 
173
    R.add_edges_from(H.edges_iter(data=True))
206
174
    return R
207
175
 
208
176
 
215
183
    emptied) or R can be a call to a graph object,
216
184
    e.g. create_using=DiGraph(). See documentation for empty_graph()
217
185
 
218
 
    Implemented for Graph, DiGraph, XGraph, XDiGraph.    
219
 
    Note that complement() is not well-defined for XGraph and XDiGraph
220
 
    objects that allow multiple edges or self-loops.
 
186
    Note that complement() does not create self-loops and also 
 
187
    does not produce parallel edges for MultiGraphs.
221
188
 
222
189
    """
223
190
    if name is None:
224
191
        name="complement(%s)"%(G.name) 
225
192
    if create_using is None:
226
 
        R=create_empty_copy(G,with_nodes=False)
 
193
        R=G.__class__()
227
194
    else:
228
195
        R=create_using
229
196
        R.clear()
230
197
    R.name=name
231
 
    R.add_nodes_from([v for v in G.nodes() ])
232
 
    for v in G.nodes():
233
 
        for u in G.nodes():
234
 
            if not G.has_edge(v,u):
235
 
                R.add_edge(v,u) 
 
198
    R.add_nodes_from(G)
 
199
    R.add_edges_from( ((n,n2) for n,nbrs in G.adjacency_iter() for n2 in G 
 
200
        if n2 not in nbrs if n is not n2) )
236
201
    return R
237
202
 
238
203
 
240
205
    """Return a copy of the graph G with all of the edges removed.
241
206
 
242
207
    """
243
 
    if hasattr(G,'allow_multiedges')==True:
244
 
        H=G.__class__(multiedges=G.multiedges,selfloops=G.selfloops)
245
 
    else:
246
 
        H=G.__class__()
 
208
    H=G.__class__()
247
209
 
248
210
    H.name='empty '+G.name
249
211
    if with_nodes:
254
216
def convert_to_undirected(G):
255
217
    """Return a new undirected representation of the graph G.
256
218
 
257
 
    Works for Graph, DiGraph, XGraph, XDiGraph.
 
219
    Works for Graph, DiGraph, MultiGraph, MultiDiGraph.
258
220
 
259
221
    Note: convert_to_undirected(G)=G.to_undirected()
260
222
 
265
227
def convert_to_directed(G):
266
228
    """Return a new directed representation of the graph G.
267
229
 
268
 
    Works for Graph, DiGraph, XGraph, XDiGraph.
 
230
    Works for Graph, DiGraph, MultiGraph, MultiDiGraph.
269
231
 
270
232
    Note: convert_to_directed(G)=G.to_directed()
271
233
    
272
234
    """
273
235
    return G.to_directed()
274
236
 
 
237
 
275
238
def relabel_nodes(G,mapping):
276
239
    """Return a copy of G with node labels transformed by mapping.
277
240
 
283
246
 
284
247
    mapping as dictionary:
285
248
 
286
 
    >>> G=path_graph(3)  # nodes 0-1-2
 
249
    Examples
 
250
    --------
 
251
 
 
252
    >>> G=nx.path_graph(3)  # nodes 0-1-2
287
253
    >>> mapping={0:'a',1:'b',2:'c'}
288
 
    >>> H=relabel_nodes(G,mapping)
 
254
    >>> H=nx.relabel_nodes(G,mapping)
289
255
    >>> print H.nodes()
290
256
    ['a', 'c', 'b']
291
257
 
292
 
    >>> G=path_graph(26) # nodes 0..25
 
258
    >>> G=nx.path_graph(26) # nodes 0..25
293
259
    >>> mapping=dict(zip(G.nodes(),"abcdefghijklmnopqrstuvwxyz"))
294
 
    >>> H=relabel_nodes(G,mapping) # nodes a..z
 
260
    >>> H=nx.relabel_nodes(G,mapping) # nodes a..z
295
261
    >>> mapping=dict(zip(G.nodes(),xrange(1,27)))
296
 
    >>> G1=relabel_nodes(G,mapping) # nodes 1..26
 
262
    >>> G1=nx.relabel_nodes(G,mapping) # nodes 1..26
297
263
 
298
264
    mapping as function
299
265
 
300
 
    >>> G=path_graph(3)
 
266
    >>> G=nx.path_graph(3)
301
267
    >>> def mapping(x):
302
268
    ...    return x**2
303
 
    >>> H=relabel_nodes(G,mapping)
 
269
    >>> H=nx.relabel_nodes(G,mapping)
304
270
    >>> print H.nodes()
305
271
    [0, 1, 4]
306
272
 
307
273
    Also see convert_node_labels_to_integers.
308
274
 
309
275
    """
310
 
    H=create_empty_copy(G,with_nodes=False)
 
276
    H=G.__class__()
311
277
    H.name="(%s)" % G.name
312
278
 
313
279
    if hasattr(mapping,"__getitem__"):   # if we are a dict
322
288
            raise networkx.NetworkXError,\
323
289
                  "relabeling function cannot be applied to node %s" % node
324
290
 
325
 
    for e in G.edges_iter():
326
 
        u=map_func(e[0])
327
 
        v=map_func(e[1])
328
 
        if len(e)==2:
329
 
            H.add_edge(u,v)
330
 
        else:
331
 
            H.add_edge(u,v,e[2])
 
291
    #for n1,n2,d in G.edges_iter(data=True):
 
292
    #    u=map_func(n1)
 
293
    #    v=map_func(n2)
 
294
    #    H.add_edge(u,v,d)
 
295
    H.add_edges_from( (map_func(n1),map_func(n2),d) for (n1,n2,d) in G.edges_iter(data=True)) 
332
296
 
333
297
    return H        
334
298
    
392
356
        nlist.sort()
393
357
        mapping=dict(zip(nlist,range(first_label,N)))
394
358
    elif ordering=="increasing degree":
395
 
        dv_pairs=[(G.degree(n),n) for n in G]
 
359
        dv_pairs=[(d,n) for (n,d) in G.degree_iter()]
396
360
        dv_pairs.sort() # in-place sort from lowest to highest degree
397
361
        mapping=dict(zip([n for d,n in dv_pairs],range(first_label,N)))
398
362
    elif ordering=="decreasing degree":
399
 
        dv_pairs=[(G.degree(n),n) for n in G]
 
363
        dv_pairs=[(d,n) for (n,d) in G.degree_iter()]
400
364
        dv_pairs.sort() # in-place sort from lowest to highest degree
401
365
        dv_pairs.reverse()
402
366
        mapping=dict(zip([n for d,n in dv_pairs],range(first_label,N)))
410
374
    if not discard_old_labels:
411
375
        H.node_labels=mapping
412
376
    return H
413
 
    
414
 
 
415
 
def _test_suite():
416
 
    import doctest
417
 
    suite = doctest.DocFileSuite('tests/operators.txt',package='networkx')
418
 
    return suite
419
 
 
420
 
if __name__ == "__main__":
421
 
    import os
422
 
    import sys
423
 
    import unittest
424
 
    if sys.version_info[:2] < (2, 4):
425
 
        print "Python version 2.4 or later required for tests (%d.%d detected)." %  sys.version_info[:2]
426
 
        sys.exit(-1)
427
 
    # directory of networkx package (relative to this)
428
 
    nxbase=sys.path[0]+os.sep+os.pardir
429
 
    sys.path.insert(0,nxbase) # prepend to search path
430
 
    unittest.TextTestRunner().run(_test_suite())
431