Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/networkx/generators/lattice.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

123 statements  

1"""Functions for generating grid graphs and lattices 

2 

3The :func:`grid_2d_graph`, :func:`triangular_lattice_graph`, and 

4:func:`hexagonal_lattice_graph` functions correspond to the three 

5`regular tilings of the plane`_, the square, triangular, and hexagonal 

6tilings, respectively. :func:`grid_graph` and :func:`hypercube_graph` 

7are similar for arbitrary dimensions. Useful relevant discussion can 

8be found about `Triangular Tiling`_, and `Square, Hex and Triangle Grids`_ 

9 

10.. _regular tilings of the plane: https://en.wikipedia.org/wiki/List_of_regular_polytopes_and_compounds#Euclidean_tilings 

11.. _Square, Hex and Triangle Grids: http://www-cs-students.stanford.edu/~amitp/game-programming/grids/ 

12.. _Triangular Tiling: https://en.wikipedia.org/wiki/Triangular_tiling 

13 

14""" 

15 

16from itertools import repeat 

17from math import sqrt 

18 

19import networkx as nx 

20from networkx.classes import set_node_attributes 

21from networkx.exception import NetworkXError 

22from networkx.generators.classic import cycle_graph, empty_graph, path_graph 

23from networkx.relabel import relabel_nodes 

24from networkx.utils import flatten, nodes_or_number, pairwise 

25 

26__all__ = [ 

27 "grid_2d_graph", 

28 "grid_graph", 

29 "hypercube_graph", 

30 "triangular_lattice_graph", 

31 "hexagonal_lattice_graph", 

32] 

33 

34 

35@nx._dispatchable(graphs=None, returns_graph=True) 

36@nodes_or_number([0, 1]) 

37def grid_2d_graph(m, n, periodic=False, create_using=None): 

38 """Returns the two-dimensional grid graph. 

39 

40 The grid graph has each node connected to its four nearest neighbors. 

41 

42 Parameters 

43 ---------- 

44 m, n : int or iterable container of nodes 

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

46 If a container, elements become the coordinate of the nodes. 

47 

48 periodic : bool or iterable 

49 If `periodic` is True, both dimensions are periodic. If False, none 

50 are periodic. If `periodic` is iterable, it should yield 2 bool 

51 values indicating whether the 1st and 2nd axes, respectively, are 

52 periodic. 

53 

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

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

56 

57 Returns 

58 ------- 

59 NetworkX graph 

60 The (possibly periodic) grid graph of the specified dimensions. 

61 

62 See Also 

63 -------- 

64 triangular_lattice_graph, hexagonal_lattice_graph : 

65 Other 2D lattice graphs 

66 grid_graph, hypercube_graph : 

67 N-dimensional lattice graphs 

68 """ 

69 G = empty_graph(0, create_using) 

70 row_name, rows = m 

71 col_name, cols = n 

72 G.add_nodes_from((i, j) for i in rows for j in cols) 

73 G.add_edges_from(((i, j), (pi, j)) for pi, i in pairwise(rows) for j in cols) 

74 G.add_edges_from(((i, j), (i, pj)) for i in rows for pj, j in pairwise(cols)) 

75 

76 try: 

77 periodic_r, periodic_c = periodic 

78 except TypeError: 

79 periodic_r = periodic_c = periodic 

80 

81 if periodic_r and len(rows) > 2: 

82 first = rows[0] 

83 last = rows[-1] 

84 G.add_edges_from(((first, j), (last, j)) for j in cols) 

85 if periodic_c and len(cols) > 2: 

86 first = cols[0] 

87 last = cols[-1] 

88 G.add_edges_from(((i, first), (i, last)) for i in rows) 

89 # both directions for directed 

90 if G.is_directed(): 

91 G.add_edges_from((v, u) for u, v in G.edges()) 

92 return G 

93 

94 

95@nx._dispatchable(graphs=None, returns_graph=True) 

96def grid_graph(dim, periodic=False): 

97 """Returns the *n*-dimensional grid graph. 

98 

99 The dimension *n* is the length of the list `dim` and the size in 

100 each dimension is the value of the corresponding list element. 

101 

102 Parameters 

103 ---------- 

104 dim : list or tuple of numbers or iterables of nodes 

105 'dim' is a tuple or list with, for each dimension, either a number 

106 that is the size of that dimension or an iterable of nodes for 

107 that dimension. The dimension of the grid_graph is the length 

108 of `dim`. 

109 

110 periodic : bool or iterable 

111 If `periodic` is True, all dimensions are periodic. If False all 

112 dimensions are not periodic. If `periodic` is iterable, it should 

113 yield `dim` bool values each of which indicates whether the 

114 corresponding axis is periodic. 

115 

116 Returns 

117 ------- 

118 NetworkX graph 

119 The (possibly periodic) grid graph of the specified dimensions. 

120 

121 See Also 

122 -------- 

123 grid_2d_graph, triangular_lattice_graph, hexagonal_lattice_graph : 

124 2D lattice graphs 

125 hypercube_graph : 

126 A special case of `grid_graph` where all elements of `dim` are identical 

127 

128 Examples 

129 -------- 

130 To produce a 2 by 3 by 4 grid graph, a graph on 24 nodes: 

131 

132 >>> from networkx import grid_graph 

133 >>> G = grid_graph(dim=(2, 3, 4)) 

134 >>> len(G) 

135 24 

136 >>> G = grid_graph(dim=(range(7, 9), range(3, 6))) 

137 >>> len(G) 

138 6 

139 """ 

140 from collections.abc import Iterable 

141 

142 from networkx.algorithms.operators.product import cartesian_product 

143 

144 if not dim: 

145 return empty_graph(0) 

146 

147 periodic = repeat(periodic) if not isinstance(periodic, Iterable) else periodic 

148 func = (cycle_graph if p else path_graph for p in periodic) 

149 

150 G = next(func)(dim[0]) 

151 for current_dim in dim[1:]: 

152 Gnew = next(func)(current_dim) 

153 G = cartesian_product(Gnew, G) 

154 # graph G is done but has labels of the form (1, (2, (3, 1))) so relabel 

155 H = relabel_nodes(G, flatten) 

156 return H 

157 

158 

159@nx._dispatchable(graphs=None, returns_graph=True) 

160def hypercube_graph(n): 

161 """Returns the *n*-dimensional hypercube graph. 

162 

163 The *n*-dimensional hypercube graph [1]_ has ``2**n`` nodes, each represented as 

164 a binary integer in the form of a tuple of 0's and 1's. Edges exist between 

165 nodes that differ in exactly one bit. 

166 

167 Parameters 

168 ---------- 

169 n : int 

170 Dimension of the hypercube, must be a positive integer. 

171 

172 Returns 

173 ------- 

174 networkx.Graph 

175 The n-dimensional hypercube graph as an undirected graph. 

176 

177 See Also 

178 -------- 

179 grid_2d_graph, triangular_lattice_graph, hexagonal_lattice_graph : 

180 2D lattice graphs 

181 grid_graph : 

182 A more general N-dimensional grid 

183 

184 Examples 

185 -------- 

186 >>> G = nx.hypercube_graph(3) 

187 >>> list(G.neighbors((0, 0, 0))) 

188 [(1, 0, 0), (0, 1, 0), (0, 0, 1)] 

189 

190 References 

191 ---------- 

192 .. [1] https://en.wikipedia.org/wiki/Hypercube_graph 

193 """ 

194 dim = n * [2] 

195 G = grid_graph(dim) 

196 return G 

197 

198 

199@nx._dispatchable(graphs=None, returns_graph=True) 

200def triangular_lattice_graph( 

201 m, n, periodic=False, with_positions=True, create_using=None 

202): 

203 r"""Returns the $m$ by $n$ triangular lattice graph. 

204 

205 The `triangular lattice graph`_ is a two-dimensional `grid graph`_ in 

206 which each square unit has a diagonal edge (each grid unit has a chord). 

207 

208 The returned graph has $m$ rows and $n$ columns of triangles. Rows and 

209 columns include both triangles pointing up and down. Rows form a strip 

210 of constant height. Columns form a series of diamond shapes, staggered 

211 with the columns on either side. Another way to state the size is that 

212 the nodes form a grid of `m+1` rows and `(n + 1) // 2` columns. 

213 The odd row nodes are shifted horizontally relative to the even rows. 

214 

215 Directed graph types have edges pointed up or right. 

216 

217 Positions of nodes are computed by default or `with_positions is True`. 

218 The position of each node (embedded in a euclidean plane) is stored in 

219 the graph using equilateral triangles with sidelength 1. 

220 The height between rows of nodes is thus $\sqrt(3)/2$. 

221 Nodes lie in the first quadrant with the node $(0, 0)$ at the origin. 

222 

223 .. _triangular lattice graph: http://mathworld.wolfram.com/TriangularGrid.html 

224 .. _grid graph: http://www-cs-students.stanford.edu/~amitp/game-programming/grids/ 

225 .. _Triangular Tiling: https://en.wikipedia.org/wiki/Triangular_tiling 

226 

227 Parameters 

228 ---------- 

229 m : int 

230 The number of rows in the lattice. 

231 

232 n : int 

233 The number of columns in the lattice. 

234 

235 periodic : bool (default: False) 

236 If True, join the boundary vertices of the grid using periodic 

237 boundary conditions. The join between boundaries is the final row 

238 and column of triangles. This means there is one row and one column 

239 fewer nodes for the periodic lattice. Periodic lattices require 

240 `m >= 3`, `n >= 5` and are allowed but misaligned if `m` or `n` are odd 

241 

242 with_positions : bool (default: True) 

243 Store the coordinates of each node in the graph node attribute 'pos'. 

244 The coordinates provide a lattice with equilateral triangles. 

245 Periodic positions shift the nodes vertically in a nonlinear way so 

246 the edges don't overlap so much. 

247 

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

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

250 

251 Returns 

252 ------- 

253 NetworkX graph 

254 The *m* by *n* triangular lattice graph. 

255 

256 See Also 

257 -------- 

258 grid_2d_graph, hexagonal_lattice_graph : 

259 Other 2D lattice graphs 

260 grid_graph, hypercube_graph : 

261 N-dimensional lattice graphs 

262 """ 

263 H = empty_graph(0, create_using) 

264 if n == 0 or m == 0: 

265 return H 

266 if periodic: 

267 if n < 5 or m < 3: 

268 msg = f"m > 2 and n > 4 required for periodic. m={m}, n={n}" 

269 raise NetworkXError(msg) 

270 

271 N = (n + 1) // 2 # number of nodes in row 

272 rows = range(m + 1) 

273 cols = range(N + 1) 

274 # Make grid 

275 H.add_edges_from(((i, j), (i + 1, j)) for j in rows for i in cols[:N]) 

276 H.add_edges_from(((i, j), (i, j + 1)) for j in rows[:m] for i in cols) 

277 # add diagonals 

278 H.add_edges_from(((i, j), (i + 1, j + 1)) for j in rows[1:m:2] for i in cols[:N]) 

279 H.add_edges_from(((i + 1, j), (i, j + 1)) for j in rows[:m:2] for i in cols[:N]) 

280 

281 # identify boundary nodes if periodic 

282 if periodic is True: 

283 for i in cols: 

284 H = nx.contracted_nodes(H, (i, 0), (i, m), store_contraction_as=None) 

285 for j in rows[:m]: 

286 H = nx.contracted_nodes(H, (0, j), (N, j), store_contraction_as=None) 

287 elif n % 2: 

288 # remove extra nodes 

289 H.remove_nodes_from((N, j) for j in rows[1::2]) 

290 

291 # Add position node attributes 

292 if with_positions: 

293 ii = (i for i in cols for j in rows) 

294 jj = (j for i in cols for j in rows) 

295 xx = (0.5 * (j % 2) + i for i in cols for j in rows) 

296 h = sqrt(3) / 2 

297 if periodic: 

298 yy = (h * j + 0.01 * i * i for i in cols for j in rows) 

299 else: 

300 yy = (h * j for i in cols for j in rows) 

301 pos = {(i, j): (x, y) for i, j, x, y in zip(ii, jj, xx, yy) if (i, j) in H} 

302 set_node_attributes(H, pos, "pos") 

303 return H 

304 

305 

306@nx._dispatchable(graphs=None, returns_graph=True) 

307def hexagonal_lattice_graph( 

308 m, n, periodic=False, with_positions=True, create_using=None 

309): 

310 """Returns an `m` by `n` hexagonal lattice graph. 

311 

312 The *hexagonal lattice graph* is a graph whose nodes and edges are 

313 the `hexagonal tiling`_ of the plane. 

314 

315 The returned graph will have `m` rows and `n` columns of hexagons. 

316 `Odd numbered columns`_ are shifted up relative to even numbered columns. 

317 

318 Positions of nodes are computed by default or `with_positions is True`. 

319 Node positions creating the standard embedding in the plane 

320 with sidelength 1 and are stored in the node attribute 'pos'. 

321 `pos = nx.get_node_attributes(G, 'pos')` creates a dict ready for drawing. 

322 

323 .. _hexagonal tiling: https://en.wikipedia.org/wiki/Hexagonal_tiling 

324 .. _Odd numbered columns: http://www-cs-students.stanford.edu/~amitp/game-programming/grids/ 

325 

326 Parameters 

327 ---------- 

328 m : int 

329 The number of rows of hexagons in the lattice. 

330 

331 n : int 

332 The number of columns of hexagons in the lattice. 

333 

334 periodic : bool 

335 Whether to make a periodic grid by joining the boundary vertices. 

336 For this to work `n` must be even and both `n > 1` and `m > 1`. 

337 The periodic connections create another row and column of hexagons 

338 so these graphs have fewer nodes as boundary nodes are identified. 

339 

340 with_positions : bool (default: True) 

341 Store the coordinates of each node in the graph node attribute 'pos'. 

342 The coordinates provide a lattice with vertical columns of hexagons 

343 offset to interleave and cover the plane. 

344 Periodic positions shift the nodes vertically in a nonlinear way so 

345 the edges don't overlap so much. 

346 

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

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

349 If graph is directed, edges will point up or right. 

350 

351 Returns 

352 ------- 

353 NetworkX graph 

354 The *m* by *n* hexagonal lattice graph. 

355 

356 See Also 

357 -------- 

358 grid_2d_graph, triangular_lattice_graph : 

359 Other 2D lattice graphs 

360 grid_graph, hypercube_graph : 

361 N-dimensional lattice graphs 

362 """ 

363 G = empty_graph(0, create_using) 

364 if m == 0 or n == 0: 

365 return G 

366 if periodic and (n % 2 == 1 or m < 2 or n < 2): 

367 msg = "periodic hexagonal lattice needs m > 1, n > 1 and even n" 

368 raise NetworkXError(msg) 

369 

370 M = 2 * m # twice as many nodes as hexagons vertically 

371 rows = range(M + 2) 

372 cols = range(n + 1) 

373 # make lattice 

374 col_edges = (((i, j), (i, j + 1)) for i in cols for j in rows[: M + 1]) 

375 row_edges = (((i, j), (i + 1, j)) for i in cols[:n] for j in rows if i % 2 == j % 2) 

376 G.add_edges_from(col_edges) 

377 G.add_edges_from(row_edges) 

378 # Remove corner nodes with one edge 

379 G.remove_node((0, M + 1)) 

380 G.remove_node((n, (M + 1) * (n % 2))) 

381 

382 # identify boundary nodes if periodic 

383 if periodic: 

384 for i in cols[:n]: 

385 G = nx.contracted_nodes(G, (i, 0), (i, M), store_contraction_as=None) 

386 for i in cols[1:]: 

387 G = nx.contracted_nodes(G, (i, 1), (i, M + 1), store_contraction_as=None) 

388 for j in rows[1:M]: 

389 G = nx.contracted_nodes(G, (0, j), (n, j), store_contraction_as=None) 

390 G.remove_node((n, M)) 

391 

392 # calc position in embedded space 

393 if with_positions: 

394 ii = (i for i in cols for j in rows) 

395 jj = (j for i in cols for j in rows) 

396 xx = (0.5 + i + i // 2 + (j % 2) * ((i % 2) - 0.5) for i in cols for j in rows) 

397 h = sqrt(3) / 2 

398 if periodic: 

399 yy = (h * j + 0.01 * i * i for i in cols for j in rows) 

400 else: 

401 yy = (h * j for i in cols for j in rows) 

402 # exclude nodes not in G 

403 pos = {(i, j): (x, y) for i, j, x, y in zip(ii, jj, xx, yy) if (i, j) in G} 

404 set_node_attributes(G, pos, "pos") 

405 return G