Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/generators/classic.py: 28%

221 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-20 07:00 +0000

1"""Generators for some classic graphs. 

2 

3The typical graph builder function is called as follows: 

4 

5>>> G = nx.complete_graph(100) 

6 

7returning the complete graph on n nodes labeled 0, .., 99 

8as a simple graph. Except for `empty_graph`, all the functions 

9in this module return a Graph class (i.e. a simple, undirected graph). 

10 

11""" 

12 

13import itertools 

14import numbers 

15 

16import networkx as nx 

17from networkx.classes import Graph 

18from networkx.exception import NetworkXError 

19from networkx.utils import nodes_or_number, pairwise 

20 

21__all__ = [ 

22 "balanced_tree", 

23 "barbell_graph", 

24 "binomial_tree", 

25 "complete_graph", 

26 "complete_multipartite_graph", 

27 "circular_ladder_graph", 

28 "circulant_graph", 

29 "cycle_graph", 

30 "dorogovtsev_goltsev_mendes_graph", 

31 "empty_graph", 

32 "full_rary_tree", 

33 "ladder_graph", 

34 "lollipop_graph", 

35 "null_graph", 

36 "path_graph", 

37 "star_graph", 

38 "trivial_graph", 

39 "turan_graph", 

40 "wheel_graph", 

41] 

42 

43 

44# ------------------------------------------------------------------- 

45# Some Classic Graphs 

46# ------------------------------------------------------------------- 

47 

48 

49def _tree_edges(n, r): 

50 if n == 0: 

51 return 

52 # helper function for trees 

53 # yields edges in rooted tree at 0 with n nodes and branching ratio r 

54 nodes = iter(range(n)) 

55 parents = [next(nodes)] # stack of max length r 

56 while parents: 

57 source = parents.pop(0) 

58 for i in range(r): 

59 try: 

60 target = next(nodes) 

61 parents.append(target) 

62 yield source, target 

63 except StopIteration: 

64 break 

65 

66 

67@nx._dispatch(graphs=None) 

68def full_rary_tree(r, n, create_using=None): 

69 """Creates a full r-ary tree of `n` nodes. 

70 

71 Sometimes called a k-ary, n-ary, or m-ary tree. 

72 "... all non-leaf nodes have exactly r children and all levels 

73 are full except for some rightmost position of the bottom level 

74 (if a leaf at the bottom level is missing, then so are all of the 

75 leaves to its right." [1]_ 

76 

77 Parameters 

78 ---------- 

79 r : int 

80 branching factor of the tree 

81 n : int 

82 Number of nodes in the tree 

83 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

84 Graph type to create. If graph instance, then cleared before populated. 

85 

86 Returns 

87 ------- 

88 G : networkx Graph 

89 An r-ary tree with n nodes 

90 

91 References 

92 ---------- 

93 .. [1] An introduction to data structures and algorithms, 

94 James Andrew Storer, Birkhauser Boston 2001, (page 225). 

95 """ 

96 G = empty_graph(n, create_using) 

97 G.add_edges_from(_tree_edges(n, r)) 

98 return G 

99 

100 

101@nx._dispatch(graphs=None) 

102def balanced_tree(r, h, create_using=None): 

103 """Returns the perfectly balanced `r`-ary tree of height `h`. 

104 

105 Parameters 

106 ---------- 

107 r : int 

108 Branching factor of the tree; each node will have `r` 

109 children. 

110 

111 h : int 

112 Height of the tree. 

113 

114 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

115 Graph type to create. If graph instance, then cleared before populated. 

116 

117 Returns 

118 ------- 

119 G : NetworkX graph 

120 A balanced `r`-ary tree of height `h`. 

121 

122 Notes 

123 ----- 

124 This is the rooted tree where all leaves are at distance `h` from 

125 the root. The root has degree `r` and all other internal nodes 

126 have degree `r + 1`. 

127 

128 Node labels are integers, starting from zero. 

129 

130 A balanced tree is also known as a *complete r-ary tree*. 

131 

132 """ 

133 # The number of nodes in the balanced tree is `1 + r + ... + r^h`, 

134 # which is computed by using the closed-form formula for a geometric 

135 # sum with ratio `r`. In the special case that `r` is 1, the number 

136 # of nodes is simply `h + 1` (since the tree is actually a path 

137 # graph). 

138 if r == 1: 

139 n = h + 1 

140 else: 

141 # This must be an integer if both `r` and `h` are integers. If 

142 # they are not, we force integer division anyway. 

143 n = (1 - r ** (h + 1)) // (1 - r) 

144 return full_rary_tree(r, n, create_using=create_using) 

145 

146 

147@nx._dispatch(graphs=None) 

148def barbell_graph(m1, m2, create_using=None): 

149 """Returns the Barbell Graph: two complete graphs connected by a path. 

150 

151 Parameters 

152 ---------- 

153 m1 : int 

154 Size of the left and right barbells, must be greater than 2. 

155 

156 m2 : int 

157 Length of the path connecting the barbells. 

158 

159 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

160 Graph type to create. If graph instance, then cleared before populated. 

161 Only undirected Graphs are supported. 

162 

163 Returns 

164 ------- 

165 G : NetworkX graph 

166 A barbell graph. 

167 

168 Notes 

169 ----- 

170 

171 

172 Two identical complete graphs $K_{m1}$ form the left and right bells, 

173 and are connected by a path $P_{m2}$. 

174 

175 The `2*m1+m2` nodes are numbered 

176 `0, ..., m1-1` for the left barbell, 

177 `m1, ..., m1+m2-1` for the path, 

178 and `m1+m2, ..., 2*m1+m2-1` for the right barbell. 

179 

180 The 3 subgraphs are joined via the edges `(m1-1, m1)` and 

181 `(m1+m2-1, m1+m2)`. If `m2=0`, this is merely two complete 

182 graphs joined together. 

183 

184 This graph is an extremal example in David Aldous 

185 and Jim Fill's e-text on Random Walks on Graphs. 

186 

187 """ 

188 if m1 < 2: 

189 raise NetworkXError("Invalid graph description, m1 should be >=2") 

190 if m2 < 0: 

191 raise NetworkXError("Invalid graph description, m2 should be >=0") 

192 

193 # left barbell 

194 G = complete_graph(m1, create_using) 

195 if G.is_directed(): 

196 raise NetworkXError("Directed Graph not supported") 

197 

198 # connecting path 

199 G.add_nodes_from(range(m1, m1 + m2 - 1)) 

200 if m2 > 1: 

201 G.add_edges_from(pairwise(range(m1, m1 + m2))) 

202 

203 # right barbell 

204 G.add_edges_from( 

205 (u, v) for u in range(m1 + m2, 2 * m1 + m2) for v in range(u + 1, 2 * m1 + m2) 

206 ) 

207 

208 # connect it up 

209 G.add_edge(m1 - 1, m1) 

210 if m2 > 0: 

211 G.add_edge(m1 + m2 - 1, m1 + m2) 

212 

213 return G 

214 

215 

216@nx._dispatch(graphs=None) 

217def binomial_tree(n, create_using=None): 

218 """Returns the Binomial Tree of order n. 

219 

220 The binomial tree of order 0 consists of a single node. A binomial tree of order k 

221 is defined recursively by linking two binomial trees of order k-1: the root of one is 

222 the leftmost child of the root of the other. 

223 

224 Parameters 

225 ---------- 

226 n : int 

227 Order of the binomial tree. 

228 

229 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

230 Graph type to create. If graph instance, then cleared before populated. 

231 

232 Returns 

233 ------- 

234 G : NetworkX graph 

235 A binomial tree of $2^n$ nodes and $2^n - 1$ edges. 

236 

237 """ 

238 G = nx.empty_graph(1, create_using) 

239 

240 N = 1 

241 for i in range(n): 

242 # Use G.edges() to ensure 2-tuples. G.edges is 3-tuple for MultiGraph 

243 edges = [(u + N, v + N) for (u, v) in G.edges()] 

244 G.add_edges_from(edges) 

245 G.add_edge(0, N) 

246 N *= 2 

247 return G 

248 

249 

250@nodes_or_number(0) 

251@nx._dispatch(graphs=None) 

252def complete_graph(n, create_using=None): 

253 """Return the complete graph `K_n` with n nodes. 

254 

255 A complete graph on `n` nodes means that all pairs 

256 of distinct nodes have an edge connecting them. 

257 

258 Parameters 

259 ---------- 

260 n : int or iterable container of nodes 

261 If n is an integer, nodes are from range(n). 

262 If n is a container of nodes, those nodes appear in the graph. 

263 Warning: n is not checked for duplicates and if present the 

264 resulting graph may not be as desired. Make sure you have no duplicates. 

265 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

266 Graph type to create. If graph instance, then cleared before populated. 

267 

268 Examples 

269 -------- 

270 >>> G = nx.complete_graph(9) 

271 >>> len(G) 

272 9 

273 >>> G.size() 

274 36 

275 >>> G = nx.complete_graph(range(11, 14)) 

276 >>> list(G.nodes()) 

277 [11, 12, 13] 

278 >>> G = nx.complete_graph(4, nx.DiGraph()) 

279 >>> G.is_directed() 

280 True 

281 

282 """ 

283 _, nodes = n 

284 G = empty_graph(nodes, create_using) 

285 if len(nodes) > 1: 

286 if G.is_directed(): 

287 edges = itertools.permutations(nodes, 2) 

288 else: 

289 edges = itertools.combinations(nodes, 2) 

290 G.add_edges_from(edges) 

291 return G 

292 

293 

294@nx._dispatch(graphs=None) 

295def circular_ladder_graph(n, create_using=None): 

296 """Returns the circular ladder graph $CL_n$ of length n. 

297 

298 $CL_n$ consists of two concentric n-cycles in which 

299 each of the n pairs of concentric nodes are joined by an edge. 

300 

301 Node labels are the integers 0 to n-1 

302 

303 """ 

304 G = ladder_graph(n, create_using) 

305 G.add_edge(0, n - 1) 

306 G.add_edge(n, 2 * n - 1) 

307 return G 

308 

309 

310@nx._dispatch(graphs=None) 

311def circulant_graph(n, offsets, create_using=None): 

312 r"""Returns the circulant graph $Ci_n(x_1, x_2, ..., x_m)$ with $n$ nodes. 

313 

314 The circulant graph $Ci_n(x_1, ..., x_m)$ consists of $n$ nodes $0, ..., n-1$ 

315 such that node $i$ is connected to nodes $(i + x) \mod n$ and $(i - x) \mod n$ 

316 for all $x$ in $x_1, ..., x_m$. Thus $Ci_n(1)$ is a cycle graph. 

317 

318 Parameters 

319 ---------- 

320 n : integer 

321 The number of nodes in the graph. 

322 offsets : list of integers 

323 A list of node offsets, $x_1$ up to $x_m$, as described above. 

324 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

325 Graph type to create. If graph instance, then cleared before populated. 

326 

327 Returns 

328 ------- 

329 NetworkX Graph of type create_using 

330 

331 Examples 

332 -------- 

333 Many well-known graph families are subfamilies of the circulant graphs; 

334 for example, to create the cycle graph on n points, we connect every 

335 node to nodes on either side (with offset plus or minus one). For n = 10, 

336 

337 >>> G = nx.circulant_graph(10, [1]) 

338 >>> edges = [ 

339 ... (0, 9), 

340 ... (0, 1), 

341 ... (1, 2), 

342 ... (2, 3), 

343 ... (3, 4), 

344 ... (4, 5), 

345 ... (5, 6), 

346 ... (6, 7), 

347 ... (7, 8), 

348 ... (8, 9), 

349 ... ] 

350 ... 

351 >>> sorted(edges) == sorted(G.edges()) 

352 True 

353 

354 Similarly, we can create the complete graph 

355 on 5 points with the set of offsets [1, 2]: 

356 

357 >>> G = nx.circulant_graph(5, [1, 2]) 

358 >>> edges = [ 

359 ... (0, 1), 

360 ... (0, 2), 

361 ... (0, 3), 

362 ... (0, 4), 

363 ... (1, 2), 

364 ... (1, 3), 

365 ... (1, 4), 

366 ... (2, 3), 

367 ... (2, 4), 

368 ... (3, 4), 

369 ... ] 

370 ... 

371 >>> sorted(edges) == sorted(G.edges()) 

372 True 

373 

374 """ 

375 G = empty_graph(n, create_using) 

376 for i in range(n): 

377 for j in offsets: 

378 G.add_edge(i, (i - j) % n) 

379 G.add_edge(i, (i + j) % n) 

380 return G 

381 

382 

383@nodes_or_number(0) 

384@nx._dispatch(graphs=None) 

385def cycle_graph(n, create_using=None): 

386 """Returns the cycle graph $C_n$ of cyclically connected nodes. 

387 

388 $C_n$ is a path with its two end-nodes connected. 

389 

390 Parameters 

391 ---------- 

392 n : int or iterable container of nodes 

393 If n is an integer, nodes are from `range(n)`. 

394 If n is a container of nodes, those nodes appear in the graph. 

395 Warning: n is not checked for duplicates and if present the 

396 resulting graph may not be as desired. Make sure you have no duplicates. 

397 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

398 Graph type to create. If graph instance, then cleared before populated. 

399 

400 Notes 

401 ----- 

402 If create_using is directed, the direction is in increasing order. 

403 

404 """ 

405 _, nodes = n 

406 G = empty_graph(nodes, create_using) 

407 G.add_edges_from(pairwise(nodes, cyclic=True)) 

408 return G 

409 

410 

411@nx._dispatch(graphs=None) 

412def dorogovtsev_goltsev_mendes_graph(n, create_using=None): 

413 """Returns the hierarchically constructed Dorogovtsev-Goltsev-Mendes graph. 

414 

415 The Dorogovtsev-Goltsev-Mendes [1]_ procedure produces a scale-free graph 

416 deterministically with the following properties for a given `n`: 

417 - Total number of nodes = ``3 * (3**n + 1) / 2`` 

418 - Total number of edges = ``3 ** (n + 1)`` 

419 

420 Parameters 

421 ---------- 

422 n : integer 

423 The generation number. 

424 

425 create_using : NetworkX Graph, optional 

426 Graph type to be returned. Directed graphs and multi graphs are not 

427 supported. 

428 

429 Returns 

430 ------- 

431 G : NetworkX Graph 

432 

433 Examples 

434 -------- 

435 >>> G = nx.dorogovtsev_goltsev_mendes_graph(3) 

436 >>> G.number_of_nodes() 

437 15 

438 >>> G.number_of_edges() 

439 27 

440 >>> nx.is_planar(G) 

441 True 

442 

443 References 

444 ---------- 

445 .. [1] S. N. Dorogovtsev, A. V. Goltsev and J. F. F. Mendes, 

446 "Pseudofractal scale-free web", Physical Review E 65, 066122, 2002. 

447 https://arxiv.org/pdf/cond-mat/0112143.pdf 

448 """ 

449 G = empty_graph(0, create_using) 

450 if G.is_directed(): 

451 raise NetworkXError("Directed Graph not supported") 

452 if G.is_multigraph(): 

453 raise NetworkXError("Multigraph not supported") 

454 

455 G.add_edge(0, 1) 

456 if n == 0: 

457 return G 

458 new_node = 2 # next node to be added 

459 for i in range(1, n + 1): # iterate over number of generations. 

460 last_generation_edges = list(G.edges()) 

461 number_of_edges_in_last_generation = len(last_generation_edges) 

462 for j in range(number_of_edges_in_last_generation): 

463 G.add_edge(new_node, last_generation_edges[j][0]) 

464 G.add_edge(new_node, last_generation_edges[j][1]) 

465 new_node += 1 

466 return G 

467 

468 

469@nodes_or_number(0) 

470@nx._dispatch(graphs=None) 

471def empty_graph(n=0, create_using=None, default=Graph): 

472 """Returns the empty graph with n nodes and zero edges. 

473 

474 Parameters 

475 ---------- 

476 n : int or iterable container of nodes (default = 0) 

477 If n is an integer, nodes are from `range(n)`. 

478 If n is a container of nodes, those nodes appear in the graph. 

479 create_using : Graph Instance, Constructor or None 

480 Indicator of type of graph to return. 

481 If a Graph-type instance, then clear and use it. 

482 If None, use the `default` constructor. 

483 If a constructor, call it to create an empty graph. 

484 default : Graph constructor (optional, default = nx.Graph) 

485 The constructor to use if create_using is None. 

486 If None, then nx.Graph is used. 

487 This is used when passing an unknown `create_using` value 

488 through your home-grown function to `empty_graph` and 

489 you want a default constructor other than nx.Graph. 

490 

491 Examples 

492 -------- 

493 >>> G = nx.empty_graph(10) 

494 >>> G.number_of_nodes() 

495 10 

496 >>> G.number_of_edges() 

497 0 

498 >>> G = nx.empty_graph("ABC") 

499 >>> G.number_of_nodes() 

500 3 

501 >>> sorted(G) 

502 ['A', 'B', 'C'] 

503 

504 Notes 

505 ----- 

506 The variable create_using should be a Graph Constructor or a 

507 "graph"-like object. Constructors, e.g. `nx.Graph` or `nx.MultiGraph` 

508 will be used to create the returned graph. "graph"-like objects 

509 will be cleared (nodes and edges will be removed) and refitted as 

510 an empty "graph" with nodes specified in n. This capability 

511 is useful for specifying the class-nature of the resulting empty 

512 "graph" (i.e. Graph, DiGraph, MyWeirdGraphClass, etc.). 

513 

514 The variable create_using has three main uses: 

515 Firstly, the variable create_using can be used to create an 

516 empty digraph, multigraph, etc. For example, 

517 

518 >>> n = 10 

519 >>> G = nx.empty_graph(n, create_using=nx.DiGraph) 

520 

521 will create an empty digraph on n nodes. 

522 

523 Secondly, one can pass an existing graph (digraph, multigraph, 

524 etc.) via create_using. For example, if G is an existing graph 

525 (resp. digraph, multigraph, etc.), then empty_graph(n, create_using=G) 

526 will empty G (i.e. delete all nodes and edges using G.clear()) 

527 and then add n nodes and zero edges, and return the modified graph. 

528 

529 Thirdly, when constructing your home-grown graph creation function 

530 you can use empty_graph to construct the graph by passing a user 

531 defined create_using to empty_graph. In this case, if you want the 

532 default constructor to be other than nx.Graph, specify `default`. 

533 

534 >>> def mygraph(n, create_using=None): 

535 ... G = nx.empty_graph(n, create_using, nx.MultiGraph) 

536 ... G.add_edges_from([(0, 1), (0, 1)]) 

537 ... return G 

538 >>> G = mygraph(3) 

539 >>> G.is_multigraph() 

540 True 

541 >>> G = mygraph(3, nx.Graph) 

542 >>> G.is_multigraph() 

543 False 

544 

545 See also create_empty_copy(G). 

546 

547 """ 

548 if create_using is None: 

549 G = default() 

550 elif isinstance(create_using, type): 

551 G = create_using() 

552 elif not hasattr(create_using, "adj"): 

553 raise TypeError("create_using is not a valid NetworkX graph type or instance") 

554 else: 

555 # create_using is a NetworkX style Graph 

556 create_using.clear() 

557 G = create_using 

558 

559 _, nodes = n 

560 G.add_nodes_from(nodes) 

561 return G 

562 

563 

564@nx._dispatch(graphs=None) 

565def ladder_graph(n, create_using=None): 

566 """Returns the Ladder graph of length n. 

567 

568 This is two paths of n nodes, with 

569 each pair connected by a single edge. 

570 

571 Node labels are the integers 0 to 2*n - 1. 

572 

573 """ 

574 G = empty_graph(2 * n, create_using) 

575 if G.is_directed(): 

576 raise NetworkXError("Directed Graph not supported") 

577 G.add_edges_from(pairwise(range(n))) 

578 G.add_edges_from(pairwise(range(n, 2 * n))) 

579 G.add_edges_from((v, v + n) for v in range(n)) 

580 return G 

581 

582 

583@nodes_or_number([0, 1]) 

584@nx._dispatch(graphs=None) 

585def lollipop_graph(m, n, create_using=None): 

586 """Returns the Lollipop Graph; `K_m` connected to `P_n`. 

587 

588 This is the Barbell Graph without the right barbell. 

589 

590 Parameters 

591 ---------- 

592 m, n : int or iterable container of nodes (default = 0) 

593 If an integer, nodes are from `range(m)` and `range(m,m+n)`. 

594 If a container of nodes, those nodes appear in the graph. 

595 Warning: m and n are not checked for duplicates and if present the 

596 resulting graph may not be as desired. Make sure you have no duplicates. 

597 

598 The nodes for m appear in the complete graph $K_m$ and the nodes 

599 for n appear in the path $P_n$ 

600 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

601 Graph type to create. If graph instance, then cleared before populated. 

602 

603 Notes 

604 ----- 

605 The 2 subgraphs are joined via an edge (m-1, m). 

606 If n=0, this is merely a complete graph. 

607 

608 (This graph is an extremal example in David Aldous and Jim 

609 Fill's etext on Random Walks on Graphs.) 

610 

611 """ 

612 m, m_nodes = m 

613 M = len(m_nodes) 

614 if M < 2: 

615 raise NetworkXError("Invalid description: m should indicate at least 2 nodes") 

616 

617 n, n_nodes = n 

618 if isinstance(m, numbers.Integral) and isinstance(n, numbers.Integral): 

619 n_nodes = list(range(M, M + n)) 

620 N = len(n_nodes) 

621 

622 # the ball 

623 G = complete_graph(m_nodes, create_using) 

624 if G.is_directed(): 

625 raise NetworkXError("Directed Graph not supported") 

626 

627 # the stick 

628 G.add_nodes_from(n_nodes) 

629 if N > 1: 

630 G.add_edges_from(pairwise(n_nodes)) 

631 

632 if len(G) != M + N: 

633 raise NetworkXError("Nodes must be distinct in containers m and n") 

634 

635 # connect ball to stick 

636 if M > 0 and N > 0: 

637 G.add_edge(m_nodes[-1], n_nodes[0]) 

638 return G 

639 

640 

641@nx._dispatch(graphs=None) 

642def null_graph(create_using=None): 

643 """Returns the Null graph with no nodes or edges. 

644 

645 See empty_graph for the use of create_using. 

646 

647 """ 

648 G = empty_graph(0, create_using) 

649 return G 

650 

651 

652@nodes_or_number(0) 

653@nx._dispatch(graphs=None) 

654def path_graph(n, create_using=None): 

655 """Returns the Path graph `P_n` of linearly connected nodes. 

656 

657 Parameters 

658 ---------- 

659 n : int or iterable 

660 If an integer, nodes are 0 to n - 1. 

661 If an iterable of nodes, in the order they appear in the path. 

662 Warning: n is not checked for duplicates and if present the 

663 resulting graph may not be as desired. Make sure you have no duplicates. 

664 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

665 Graph type to create. If graph instance, then cleared before populated. 

666 

667 """ 

668 _, nodes = n 

669 G = empty_graph(nodes, create_using) 

670 G.add_edges_from(pairwise(nodes)) 

671 return G 

672 

673 

674@nodes_or_number(0) 

675@nx._dispatch(graphs=None) 

676def star_graph(n, create_using=None): 

677 """Return the star graph 

678 

679 The star graph consists of one center node connected to n outer nodes. 

680 

681 Parameters 

682 ---------- 

683 n : int or iterable 

684 If an integer, node labels are 0 to n with center 0. 

685 If an iterable of nodes, the center is the first. 

686 Warning: n is not checked for duplicates and if present the 

687 resulting graph may not be as desired. Make sure you have no duplicates. 

688 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

689 Graph type to create. If graph instance, then cleared before populated. 

690 

691 Notes 

692 ----- 

693 The graph has n+1 nodes for integer n. 

694 So star_graph(3) is the same as star_graph(range(4)). 

695 """ 

696 n, nodes = n 

697 if isinstance(n, numbers.Integral): 

698 nodes.append(n) # there should be n+1 nodes 

699 G = empty_graph(nodes, create_using) 

700 if G.is_directed(): 

701 raise NetworkXError("Directed Graph not supported") 

702 

703 if len(nodes) > 1: 

704 hub, *spokes = nodes 

705 G.add_edges_from((hub, node) for node in spokes) 

706 return G 

707 

708 

709@nx._dispatch(graphs=None) 

710def trivial_graph(create_using=None): 

711 """Return the Trivial graph with one node (with label 0) and no edges.""" 

712 G = empty_graph(1, create_using) 

713 return G 

714 

715 

716@nx._dispatch(graphs=None) 

717def turan_graph(n, r): 

718 r"""Return the Turan Graph 

719 

720 The Turan Graph is a complete multipartite graph on $n$ nodes 

721 with $r$ disjoint subsets. That is, edges connect each node to 

722 every node not in its subset. 

723 

724 Given $n$ and $r$, we create a complete multipartite graph with 

725 $r-(n \mod r)$ partitions of size $n/r$, rounded down, and 

726 $n \mod r$ partitions of size $n/r+1$, rounded down. 

727 

728 Parameters 

729 ---------- 

730 n : int 

731 The number of nodes. 

732 r : int 

733 The number of partitions. 

734 Must be less than or equal to n. 

735 

736 Notes 

737 ----- 

738 Must satisfy $1 <= r <= n$. 

739 The graph has $(r-1)(n^2)/(2r)$ edges, rounded down. 

740 """ 

741 

742 if not 1 <= r <= n: 

743 raise NetworkXError("Must satisfy 1 <= r <= n") 

744 

745 partitions = [n // r] * (r - (n % r)) + [n // r + 1] * (n % r) 

746 G = complete_multipartite_graph(*partitions) 

747 return G 

748 

749 

750@nodes_or_number(0) 

751@nx._dispatch(graphs=None) 

752def wheel_graph(n, create_using=None): 

753 """Return the wheel graph 

754 

755 The wheel graph consists of a hub node connected to a cycle of (n-1) nodes. 

756 

757 Parameters 

758 ---------- 

759 n : int or iterable 

760 If an integer, node labels are 0 to n with center 0. 

761 If an iterable of nodes, the center is the first. 

762 Warning: n is not checked for duplicates and if present the 

763 resulting graph may not be as desired. Make sure you have no duplicates. 

764 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

765 Graph type to create. If graph instance, then cleared before populated. 

766 

767 Node labels are the integers 0 to n - 1. 

768 """ 

769 _, nodes = n 

770 G = empty_graph(nodes, create_using) 

771 if G.is_directed(): 

772 raise NetworkXError("Directed Graph not supported") 

773 

774 if len(nodes) > 1: 

775 hub, *rim = nodes 

776 G.add_edges_from((hub, node) for node in rim) 

777 if len(rim) > 1: 

778 G.add_edges_from(pairwise(rim, cyclic=True)) 

779 return G 

780 

781 

782@nx._dispatch(graphs=None) 

783def complete_multipartite_graph(*subset_sizes): 

784 """Returns the complete multipartite graph with the specified subset sizes. 

785 

786 Parameters 

787 ---------- 

788 subset_sizes : tuple of integers or tuple of node iterables 

789 The arguments can either all be integer number of nodes or they 

790 can all be iterables of nodes. If integers, they represent the 

791 number of nodes in each subset of the multipartite graph. 

792 If iterables, each is used to create the nodes for that subset. 

793 The length of subset_sizes is the number of subsets. 

794 

795 Returns 

796 ------- 

797 G : NetworkX Graph 

798 Returns the complete multipartite graph with the specified subsets. 

799 

800 For each node, the node attribute 'subset' is an integer 

801 indicating which subset contains the node. 

802 

803 Examples 

804 -------- 

805 Creating a complete tripartite graph, with subsets of one, two, and three 

806 nodes, respectively. 

807 

808 >>> G = nx.complete_multipartite_graph(1, 2, 3) 

809 >>> [G.nodes[u]["subset"] for u in G] 

810 [0, 1, 1, 2, 2, 2] 

811 >>> list(G.edges(0)) 

812 [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)] 

813 >>> list(G.edges(2)) 

814 [(2, 0), (2, 3), (2, 4), (2, 5)] 

815 >>> list(G.edges(4)) 

816 [(4, 0), (4, 1), (4, 2)] 

817 

818 >>> G = nx.complete_multipartite_graph("a", "bc", "def") 

819 >>> [G.nodes[u]["subset"] for u in sorted(G)] 

820 [0, 1, 1, 2, 2, 2] 

821 

822 Notes 

823 ----- 

824 This function generalizes several other graph builder functions. 

825 

826 - If no subset sizes are given, this returns the null graph. 

827 - If a single subset size `n` is given, this returns the empty graph on 

828 `n` nodes. 

829 - If two subset sizes `m` and `n` are given, this returns the complete 

830 bipartite graph on `m + n` nodes. 

831 - If subset sizes `1` and `n` are given, this returns the star graph on 

832 `n + 1` nodes. 

833 

834 See also 

835 -------- 

836 complete_bipartite_graph 

837 """ 

838 # The complete multipartite graph is an undirected simple graph. 

839 G = Graph() 

840 

841 if len(subset_sizes) == 0: 

842 return G 

843 

844 # set up subsets of nodes 

845 try: 

846 extents = pairwise(itertools.accumulate((0,) + subset_sizes)) 

847 subsets = [range(start, end) for start, end in extents] 

848 except TypeError: 

849 subsets = subset_sizes 

850 

851 # add nodes with subset attribute 

852 # while checking that ints are not mixed with iterables 

853 try: 

854 for i, subset in enumerate(subsets): 

855 G.add_nodes_from(subset, subset=i) 

856 except TypeError as err: 

857 raise NetworkXError("Arguments must be all ints or all iterables") from err 

858 

859 # Across subsets, all nodes should be adjacent. 

860 # We can use itertools.combinations() because undirected. 

861 for subset1, subset2 in itertools.combinations(subsets, 2): 

862 G.add_edges_from(itertools.product(subset1, subset2)) 

863 return G