~ubuntu-branches/ubuntu/raring/python-networkx/raring

« back to all changes in this revision

Viewing changes to networkx/algorithms/operators.py

  • Committer: Package Import Robot
  • Author(s): Sandro Tosi
  • Date: 2012-08-11 12:41:30 UTC
  • mfrom: (1.4.1) (5.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20120811124130-whr6uso7fncyg8bi
Tags: 1.7-1
* New upstream release
* debian/patches/changeset_*
  - removed, included upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
Operations on graphs including union, intersection, difference,
3
 
complement, subgraph. 
4
 
"""
5
 
#    Copyright (C) 2004-2010 by 
6
 
#    Aric Hagberg <hagberg@lanl.gov>
7
 
#    Dan Schult <dschult@colgate.edu>
8
 
#    Pieter Swart <swart@lanl.gov>
9
 
#    All rights reserved.
10
 
#    BSD license.
11
 
__author__ = """\n""".join(['Aric Hagberg (hagberg@lanl.gov)',
12
 
                           'Pieter Swart (swart@lanl.gov)',
13
 
                           'Dan Schult(dschult@colgate.edu)'])
14
 
 
15
 
__all__ = ['union', 'cartesian_product', 
16
 
           'compose', 'complement',
17
 
           'disjoint_union', 'intersection', 
18
 
           'difference', 'symmetric_difference']
19
 
 
20
 
import networkx as nx
21
 
from networkx.utils import is_string_like
22
 
                                                                                
23
 
 
24
 
def union(G,H,create_using=None,rename=False,name=None):
25
 
    """ Return the union of graphs G and H.
26
 
    
27
 
    Graphs G and H must be disjoint, otherwise an exception is raised.
28
 
 
29
 
    Parameters
30
 
    ----------
31
 
    G,H : graph
32
 
       A NetworkX graph 
33
 
 
34
 
    create_using : NetworkX graph
35
 
       Use specified graph for result.  Otherwise a new graph is created
36
 
       with the same type as G.
37
 
 
38
 
    rename : bool (default=False)         
39
 
       Node names of G and H can be changed be specifying the tuple
40
 
       rename=('G-','H-') (for example).  Node u in G is then renamed
41
 
       "G-u" and v in H is renamed "H-v".
42
 
 
43
 
    name : string       
44
 
       Specify the name for the union graph
45
 
 
46
 
    Notes       
47
 
    -----
48
 
    To force a disjoint union with node relabeling, use
49
 
    disjoint_union(G,H) or convert_node_labels_to integers().
50
 
 
51
 
    Graph, edge, and node attributes are propagated from G and H
52
 
    to the union graph.  If a graph attribute is present in both
53
 
    G and H the value from G is used.
54
 
 
55
 
 
56
 
    See Also
57
 
    --------
58
 
    disjoint_union
59
 
 
60
 
    """
61
 
    if name is None:
62
 
        name="union( %s, %s )"%(G.name,H.name)
63
 
    if create_using is None:
64
 
        R=G.__class__()
65
 
    else:
66
 
        R=create_using
67
 
        R.clear()
68
 
    R.name=name
69
 
 
70
 
    # rename graph to obtain disjoint node labels
71
 
    if rename: # create new string labels
72
 
        def add_prefix0(x):
73
 
            prefix=rename[0]
74
 
            if is_string_like(x):
75
 
                name=prefix+x
76
 
            else:
77
 
                name=prefix+repr(x)
78
 
            return name
79
 
        def add_prefix1(x):
80
 
            prefix=rename[1]
81
 
            if is_string_like(x):
82
 
                name=prefix+x
83
 
            else:
84
 
                name=prefix+repr(x)
85
 
            return name
86
 
        G=nx.relabel_nodes(G,add_prefix0)
87
 
        H=nx.relabel_nodes(H,add_prefix1)
88
 
 
89
 
    if set(G) & set(H):
90
 
        raise nx.NetworkXError(\
91
 
            """The node sets of G and H are not disjoint. 
92
 
Use appropriate rename=('Gprefix','Hprefix') or use disjoint_union(G,H).""")
93
 
    # node names OK, now build union
94
 
    R.add_nodes_from(G)
95
 
    if G.is_multigraph():
96
 
        R.add_edges_from(e for e in G.edges_iter(keys=True,data=True))
97
 
    else:
98
 
        R.add_edges_from(e for e in G.edges_iter(data=True))
99
 
    R.add_nodes_from(H)
100
 
    if H.is_multigraph():
101
 
        R.add_edges_from(e for e in H.edges_iter(keys=True,data=True))
102
 
    else:
103
 
        R.add_edges_from(e for e in H.edges_iter(data=True))
104
 
 
105
 
    # add node attributes
106
 
    R.node.update(G.node)
107
 
    R.node.update(H.node)
108
 
    # add graph attributes, G attributes take precedent over H attributes
109
 
    R.graph.update(H.graph)
110
 
    R.graph.update(G.graph)
111
 
    return R
112
 
 
113
 
 
114
 
def disjoint_union(G,H):
115
 
    """ Return the disjoint union of graphs G and H, forcing distinct integer node labels.
116
 
 
117
 
    Parameters
118
 
    ----------
119
 
    G,H : graph
120
 
       A NetworkX graph 
121
 
 
122
 
    Notes
123
 
    -----
124
 
    A new graph is created, of the same class as G.  It is recommended
125
 
    that G and H be either both directed or both undirected.
126
 
 
127
 
    """
128
 
    R1=nx.convert_node_labels_to_integers(G)
129
 
    R2=nx.convert_node_labels_to_integers(H,first_label=len(R1))
130
 
    R=union(R1,R2)
131
 
    R.name="disjoint_union( %s, %s )"%(G.name,H.name)
132
 
    return R
133
 
 
134
 
 
135
 
def intersection(G,H,create_using=None ):
136
 
    """Return a new graph that contains only the edges that exist in both G and H.   
137
 
 
138
 
    The node sets of H and G must be the same.
139
 
 
140
 
    Parameters
141
 
    ----------
142
 
    G,H : graph
143
 
       A NetworkX graph.  G and H must have the same node sets. 
144
 
 
145
 
    create_using : NetworkX graph
146
 
       Use specified graph for result.  Otherwise a new graph is created
147
 
       with the same type as G.
148
 
 
149
 
    Notes
150
 
    -----
151
 
    Attributes from the graph, nodes, and edges are not copied to the new
152
 
    graph.  If you want a new graph of the intersection of G and H
153
 
    with the attributes (including edge data) from G use remove_nodes_from()
154
 
    as follows
155
 
 
156
 
    >>> G=nx.path_graph(3)
157
 
    >>> H=nx.path_graph(5)
158
 
    >>> R=G.copy()
159
 
    >>> R.remove_nodes_from(n for n in G if n not in H)
160
 
 
161
 
    """
162
 
    # create new graph         
163
 
    if create_using is None:  # Graph object of the same type as G
164
 
        R=nx.create_empty_copy(G)
165
 
    else:                     # user specified graph 
166
 
        R=create_using 
167
 
        R.clear() 
168
 
 
169
 
    R.name="Intersection of (%s and %s)"%(G.name, H.name)
170
 
 
171
 
    if set(G)!=set(H):
172
 
        raise nx.NetworkXError("Node sets of graphs are not equal")     
173
 
    
174
 
    if G.number_of_edges()<=H.number_of_edges():
175
 
        if G.is_multigraph():
176
 
            edges=G.edges_iter(keys=True)
177
 
        else:
178
 
            edges=G.edges_iter()
179
 
        for e in edges:
180
 
            if H.has_edge(*e):
181
 
                R.add_edge(*e)
182
 
    else:
183
 
        if H.is_multigraph():
184
 
            edges=H.edges_iter(keys=True)
185
 
        else:
186
 
            edges=H.edges_iter()
187
 
        for e in edges:
188
 
            if G.has_edge(*e):
189
 
                R.add_edge(*e)
190
 
 
191
 
    return R
192
 
 
193
 
def difference(G,H,create_using=None):
194
 
    """Return a new graph that contains the edges that exist in G 
195
 
    but not in H.  
196
 
 
197
 
    The node sets of H and G must be the same.
198
 
 
199
 
    Parameters
200
 
    ----------
201
 
    G,H : graph
202
 
       A NetworkX graph.  G and H must have the same node sets. 
203
 
 
204
 
    create_using : NetworkX graph
205
 
       Use specified graph for result.  Otherwise a new graph is created
206
 
       with the same type as G.
207
 
 
208
 
    Notes
209
 
    -----
210
 
    Attributes from the graph, nodes, and edges are not copied to the new
211
 
    graph.  If you want a new graph of the difference of G and H with
212
 
    with the attributes (including edge data) from G use remove_nodes_from()
213
 
    as follows
214
 
 
215
 
    >>> G=nx.path_graph(3)
216
 
    >>> H=nx.path_graph(5)
217
 
    >>> R=G.copy()
218
 
    >>> R.remove_nodes_from(n for n in G if n in H)
219
 
 
220
 
    """
221
 
    # create new graph         
222
 
    if create_using is None:  # Graph object of the same type as G
223
 
        R=nx.create_empty_copy(G)
224
 
    else:                     # user specified graph 
225
 
        R=create_using 
226
 
        R.clear() 
227
 
 
228
 
    R.name="Difference of (%s and %s)"%(G.name, H.name)
229
 
 
230
 
    if set(G)!=set(H):
231
 
        raise nx.NetworkXError("Node sets of graphs not equal")        
232
 
    
233
 
    if G.is_multigraph():
234
 
        edges=G.edges_iter(keys=True)
235
 
    else:
236
 
        edges=G.edges_iter()
237
 
    for e in edges:
238
 
        if not H.has_edge(*e):
239
 
            R.add_edge(*e)
240
 
    return R
241
 
 
242
 
def symmetric_difference(G,H,create_using=None ):
243
 
    """Return new graph with edges that exist in either G or H but not both.
244
 
 
245
 
    The node sets of H and G must be the same.
246
 
 
247
 
    Parameters
248
 
    ----------
249
 
    G,H : graph
250
 
       A NetworkX graph.  G and H must have the same node sets. 
251
 
 
252
 
    create_using : NetworkX graph
253
 
       Use specified graph for result.  Otherwise a new graph is created
254
 
       with the same type as G.
255
 
 
256
 
    Notes
257
 
    -----
258
 
    Attributes from the graph, nodes, and edges are not copied to the new
259
 
    graph. 
260
 
    """
261
 
    # create new graph         
262
 
    if create_using is None:  # Graph object of the same type as G
263
 
        R=nx.create_empty_copy(G)
264
 
    else:                     # user specified graph 
265
 
        R=create_using 
266
 
        R.clear() 
267
 
 
268
 
    R.name="Symmetric difference of (%s and %s)"%(G.name, H.name)
269
 
 
270
 
    if set(G)!=set(H):
271
 
        raise nx.NetworkXError("Node sets of graphs not equal")        
272
 
    
273
 
    gnodes=set(G) # set of nodes in G
274
 
    hnodes=set(H) # set of nodes in H
275
 
    nodes=gnodes.symmetric_difference(hnodes)
276
 
    R.add_nodes_from(nodes)
277
 
    
278
 
    if G.is_multigraph():
279
 
        edges=G.edges_iter(keys=True)
280
 
    else:
281
 
        edges=G.edges_iter()
282
 
    # we could copy the data here but then this function doesn't
283
 
    # match intersection and difference
284
 
    for e in edges:
285
 
        if not H.has_edge(*e):
286
 
            R.add_edge(*e)
287
 
 
288
 
    if H.is_multigraph():
289
 
        edges=H.edges_iter(keys=True)
290
 
    else:
291
 
        edges=H.edges_iter()
292
 
    for e in edges:
293
 
        if not G.has_edge(*e):
294
 
            R.add_edge(*e)
295
 
    return R
296
 
 
297
 
def cartesian_product(G,H,create_using=None):
298
 
    """ Return the Cartesian product of G and H.
299
 
 
300
 
    Parameters
301
 
    ----------
302
 
    G,H : graph
303
 
       A NetworkX graph 
304
 
 
305
 
    create_using : NetworkX graph
306
 
       Use specified graph for result.  Otherwise a new graph is created
307
 
       with the same type as G.
308
 
 
309
 
    Notes
310
 
    -----
311
 
    Only tested with Graph class.  Graph, node, and edge attributes
312
 
    are not copied to the new graph.
313
 
    """
314
 
    if create_using is None:
315
 
        Prod=G.__class__()
316
 
    else:
317
 
        Prod=create_using
318
 
        Prod.clear()
319
 
 
320
 
    for v in G:
321
 
        for w in H:
322
 
            Prod.add_node((v,w)) 
323
 
 
324
 
    if G.is_multigraph():
325
 
        Prod.add_edges_from( (((v,w1),(v,w2),k,d) 
326
 
                              for w1,w2,k,d 
327
 
                              in H.edges_iter(keys=True,data=True) 
328
 
                              for v in G) )
329
 
        Prod.add_edges_from( (((v1,w),(v2,w),k,d) 
330
 
                              for v1,v2,k,d 
331
 
                              in G.edges_iter(keys=True,data=True) 
332
 
                              for w in H) )
333
 
 
334
 
    else:
335
 
        Prod.add_edges_from( (((v,w1),(v,w2),d) 
336
 
                              for w1,w2,d in H.edges_iter(data=True) 
337
 
                              for v in G) )
338
 
        Prod.add_edges_from( (((v1,w),(v2,w),d) 
339
 
                              for v1,v2,d in G.edges_iter(data=True) 
340
 
                              for w in H) )
341
 
 
342
 
    Prod.name="Cartesian Product("+G.name+","+H.name+")"
343
 
    return Prod
344
 
 
345
 
 
346
 
def compose(G,H,create_using=None, name=None):
347
 
    """ Return a new graph of G composed with H.
348
 
    
349
 
    Composition is the simple union of the node sets and edge sets.
350
 
    The node sets of G and H need not be disjoint.
351
 
 
352
 
    Parameters
353
 
    ----------
354
 
    G,H : graph
355
 
       A NetworkX graph 
356
 
 
357
 
    create_using : NetworkX graph
358
 
       Use specified graph for result.  Otherwise a new graph is created
359
 
       with the same type as G
360
 
 
361
 
    name : string       
362
 
       Specify name for new graph
363
 
 
364
 
    Notes
365
 
    -----
366
 
    A new graph is returned, of the same class as G.  It is
367
 
    recommended that G and H be either both directed or both
368
 
    undirected.  Attributes from G take precedent over attributes
369
 
    from H.
370
 
 
371
 
    """
372
 
    if name is None:
373
 
        name="compose( %s, %s )"%(G.name,H.name)
374
 
    if create_using is None:
375
 
        R=G.__class__()
376
 
    else:
377
 
        R=create_using
378
 
        R.clear()
379
 
    R.name=name
380
 
    R.add_nodes_from(H.nodes())
381
 
    R.add_nodes_from(G.nodes())
382
 
    if H.is_multigraph():
383
 
        R.add_edges_from(H.edges_iter(keys=True,data=True))
384
 
    else:
385
 
        R.add_edges_from(H.edges_iter(data=True))
386
 
    if G.is_multigraph():
387
 
        R.add_edges_from(G.edges_iter(keys=True,data=True))
388
 
    else:
389
 
        R.add_edges_from(G.edges_iter(data=True))
390
 
 
391
 
    # add node attributes, G attributes take precedent over H attributes
392
 
    R.node.update(H.node)
393
 
    R.node.update(G.node)
394
 
    # add graph attributes, G attributes take precedent over H attributes
395
 
    R.graph.update(H.graph)
396
 
    R.graph.update(G.graph)
397
 
 
398
 
    return R
399
 
 
400
 
 
401
 
def complement(G,create_using=None,name=None):
402
 
    """ Return graph complement of G.
403
 
 
404
 
    Parameters
405
 
    ----------
406
 
    G : graph
407
 
       A NetworkX graph 
408
 
 
409
 
    create_using : NetworkX graph
410
 
       Use specified graph for result.  Otherwise a new graph is created.
411
 
 
412
 
    name : string       
413
 
       Specify name for new graph
414
 
 
415
 
    Notes
416
 
    ------
417
 
    Note that complement() does not create self-loops and also 
418
 
    does not produce parallel edges for MultiGraphs.
419
 
 
420
 
    Graph, node, and edge data are not propagated to the new graph.
421
 
    """
422
 
    if name is None:
423
 
        name="complement(%s)"%(G.name) 
424
 
    if create_using is None:
425
 
        R=G.__class__()
426
 
    else:
427
 
        R=create_using
428
 
        R.clear()
429
 
    R.name=name
430
 
    R.add_nodes_from(G)
431
 
    R.add_edges_from( ((n,n2) 
432
 
                       for n,nbrs in G.adjacency_iter() 
433
 
                       for n2 in G if n2 not in nbrs 
434
 
                       if n is not n2) )
435
 
    return R
436
 
 
437
 
 
438