test_latex.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import pytest
  2. import networkx as nx
  3. def test_tikz_attributes():
  4. G = nx.path_graph(4, create_using=nx.DiGraph)
  5. pos = {n: (n, n) for n in G}
  6. G.add_edge(0, 0)
  7. G.edges[(0, 0)]["label"] = "Loop"
  8. G.edges[(0, 0)]["label_options"] = "midway"
  9. G.nodes[0]["style"] = "blue"
  10. G.nodes[1]["style"] = "line width=3,draw"
  11. G.nodes[2]["style"] = "circle,draw,blue!50"
  12. G.nodes[3]["label"] = "Stop"
  13. G.edges[(0, 1)]["label"] = "1st Step"
  14. G.edges[(0, 1)]["label_options"] = "near end"
  15. G.edges[(2, 3)]["label"] = "3rd Step"
  16. G.edges[(2, 3)]["label_options"] = "near start"
  17. G.edges[(2, 3)]["style"] = "bend left,green"
  18. G.edges[(1, 2)]["label"] = "2nd"
  19. G.edges[(1, 2)]["label_options"] = "pos=0.5"
  20. G.edges[(1, 2)]["style"] = ">->,bend right,line width=3,green!90"
  21. output_tex = nx.to_latex(
  22. G,
  23. pos=pos,
  24. as_document=False,
  25. tikz_options="[scale=3]",
  26. node_options="style",
  27. edge_options="style",
  28. node_label="label",
  29. edge_label="label",
  30. edge_label_options="label_options",
  31. )
  32. expected_tex = r"""\begin{figure}
  33. \begin{tikzpicture}[scale=3]
  34. \draw
  35. (0, 0) node[blue] (0){0}
  36. (1, 1) node[line width=3,draw] (1){1}
  37. (2, 2) node[circle,draw,blue!50] (2){2}
  38. (3, 3) node (3){Stop};
  39. \begin{scope}[->]
  40. \draw (0) to node[near end] {1st Step} (1);
  41. \draw[loop,] (0) to node[midway] {Loop} (0);
  42. \draw[>->,bend right,line width=3,green!90] (1) to node[pos=0.5] {2nd} (2);
  43. \draw[bend left,green] (2) to node[near start] {3rd Step} (3);
  44. \end{scope}
  45. \end{tikzpicture}
  46. \end{figure}"""
  47. assert output_tex == expected_tex
  48. # print(output_tex)
  49. # # Pretty way to assert that A.to_document() == expected_tex
  50. # content_same = True
  51. # for aa, bb in zip(expected_tex.split("\n"), output_tex.split("\n")):
  52. # if aa != bb:
  53. # content_same = False
  54. # print(f"-{aa}|\n+{bb}|")
  55. # assert content_same
  56. def test_basic_multiple_graphs():
  57. H1 = nx.path_graph(4)
  58. H2 = nx.complete_graph(4)
  59. H3 = nx.path_graph(8)
  60. H4 = nx.complete_graph(8)
  61. captions = [
  62. "Path on 4 nodes",
  63. "Complete graph on 4 nodes",
  64. "Path on 8 nodes",
  65. "Complete graph on 8 nodes",
  66. ]
  67. labels = ["fig2a", "fig2b", "fig2c", "fig2d"]
  68. latex_code = nx.to_latex(
  69. [H1, H2, H3, H4],
  70. n_rows=2,
  71. sub_captions=captions,
  72. sub_labels=labels,
  73. )
  74. # print(latex_code)
  75. assert "begin{document}" in latex_code
  76. assert "begin{figure}" in latex_code
  77. assert latex_code.count("begin{subfigure}") == 4
  78. assert latex_code.count("tikzpicture") == 8
  79. assert latex_code.count("[-]") == 4
  80. def test_basic_tikz():
  81. expected_tex = r"""\documentclass{report}
  82. \usepackage{tikz}
  83. \usepackage{subcaption}
  84. \begin{document}
  85. \begin{figure}
  86. \begin{subfigure}{0.5\textwidth}
  87. \begin{tikzpicture}[scale=2]
  88. \draw[gray!90]
  89. (0.749, 0.702) node[red!90] (0){0}
  90. (1.0, -0.014) node[red!90] (1){1}
  91. (-0.777, -0.705) node (2){2}
  92. (-0.984, 0.042) node (3){3}
  93. (-0.028, 0.375) node[cyan!90] (4){4}
  94. (-0.412, 0.888) node (5){5}
  95. (0.448, -0.856) node (6){6}
  96. (0.003, -0.431) node[cyan!90] (7){7};
  97. \begin{scope}[->,gray!90]
  98. \draw (0) to (4);
  99. \draw (0) to (5);
  100. \draw (0) to (6);
  101. \draw (0) to (7);
  102. \draw (1) to (4);
  103. \draw (1) to (5);
  104. \draw (1) to (6);
  105. \draw (1) to (7);
  106. \draw (2) to (4);
  107. \draw (2) to (5);
  108. \draw (2) to (6);
  109. \draw (2) to (7);
  110. \draw (3) to (4);
  111. \draw (3) to (5);
  112. \draw (3) to (6);
  113. \draw (3) to (7);
  114. \end{scope}
  115. \end{tikzpicture}
  116. \caption{My tikz number 1 of 2}\label{tikz_1_2}
  117. \end{subfigure}
  118. \begin{subfigure}{0.5\textwidth}
  119. \begin{tikzpicture}[scale=2]
  120. \draw[gray!90]
  121. (0.749, 0.702) node[green!90] (0){0}
  122. (1.0, -0.014) node[green!90] (1){1}
  123. (-0.777, -0.705) node (2){2}
  124. (-0.984, 0.042) node (3){3}
  125. (-0.028, 0.375) node[purple!90] (4){4}
  126. (-0.412, 0.888) node (5){5}
  127. (0.448, -0.856) node (6){6}
  128. (0.003, -0.431) node[purple!90] (7){7};
  129. \begin{scope}[->,gray!90]
  130. \draw (0) to (4);
  131. \draw (0) to (5);
  132. \draw (0) to (6);
  133. \draw (0) to (7);
  134. \draw (1) to (4);
  135. \draw (1) to (5);
  136. \draw (1) to (6);
  137. \draw (1) to (7);
  138. \draw (2) to (4);
  139. \draw (2) to (5);
  140. \draw (2) to (6);
  141. \draw (2) to (7);
  142. \draw (3) to (4);
  143. \draw (3) to (5);
  144. \draw (3) to (6);
  145. \draw (3) to (7);
  146. \end{scope}
  147. \end{tikzpicture}
  148. \caption{My tikz number 2 of 2}\label{tikz_2_2}
  149. \end{subfigure}
  150. \caption{A graph generated with python and latex.}
  151. \end{figure}
  152. \end{document}"""
  153. edges = [
  154. (0, 4),
  155. (0, 5),
  156. (0, 6),
  157. (0, 7),
  158. (1, 4),
  159. (1, 5),
  160. (1, 6),
  161. (1, 7),
  162. (2, 4),
  163. (2, 5),
  164. (2, 6),
  165. (2, 7),
  166. (3, 4),
  167. (3, 5),
  168. (3, 6),
  169. (3, 7),
  170. ]
  171. G = nx.DiGraph()
  172. G.add_nodes_from(range(8))
  173. G.add_edges_from(edges)
  174. pos = {
  175. 0: (0.7490296171687696, 0.702353520257394),
  176. 1: (1.0, -0.014221357723796535),
  177. 2: (-0.7765783344161441, -0.7054170966808919),
  178. 3: (-0.9842690223417624, 0.04177547602465483),
  179. 4: (-0.02768523817180917, 0.3745724439551441),
  180. 5: (-0.41154855146767433, 0.8880106515525136),
  181. 6: (0.44780153389148264, -0.8561492709269164),
  182. 7: (0.0032499953371383505, -0.43092436645809945),
  183. }
  184. rc_node_color = {0: "red!90", 1: "red!90", 4: "cyan!90", 7: "cyan!90"}
  185. gp_node_color = {0: "green!90", 1: "green!90", 4: "purple!90", 7: "purple!90"}
  186. H = G.copy()
  187. nx.set_node_attributes(G, rc_node_color, "color")
  188. nx.set_node_attributes(H, gp_node_color, "color")
  189. sub_captions = ["My tikz number 1 of 2", "My tikz number 2 of 2"]
  190. sub_labels = ["tikz_1_2", "tikz_2_2"]
  191. output_tex = nx.to_latex(
  192. [G, H],
  193. [pos, pos],
  194. tikz_options="[scale=2]",
  195. default_node_options="gray!90",
  196. default_edge_options="gray!90",
  197. node_options="color",
  198. sub_captions=sub_captions,
  199. sub_labels=sub_labels,
  200. caption="A graph generated with python and latex.",
  201. n_rows=2,
  202. as_document=True,
  203. )
  204. assert output_tex == expected_tex
  205. # print(output_tex)
  206. # # Pretty way to assert that A.to_document() == expected_tex
  207. # content_same = True
  208. # for aa, bb in zip(expected_tex.split("\n"), output_tex.split("\n")):
  209. # if aa != bb:
  210. # content_same = False
  211. # print(f"-{aa}|\n+{bb}|")
  212. # assert content_same
  213. def test_exception_pos_single_graph(to_latex=nx.to_latex):
  214. # smoke test that pos can be a string
  215. G = nx.path_graph(4)
  216. to_latex(G, pos="pos")
  217. # must include all nodes
  218. pos = {0: (1, 2), 1: (0, 1), 2: (2, 1)}
  219. with pytest.raises(nx.NetworkXError):
  220. to_latex(G, pos)
  221. # must have 2 values
  222. pos[3] = (1, 2, 3)
  223. with pytest.raises(nx.NetworkXError):
  224. to_latex(G, pos)
  225. pos[3] = 2
  226. with pytest.raises(nx.NetworkXError):
  227. to_latex(G, pos)
  228. # check that passes with 2 values
  229. pos[3] = (3, 2)
  230. to_latex(G, pos)
  231. def test_exception_multiple_graphs(to_latex=nx.to_latex):
  232. G = nx.path_graph(3)
  233. pos_bad = {0: (1, 2), 1: (0, 1)}
  234. pos_OK = {0: (1, 2), 1: (0, 1), 2: (2, 1)}
  235. fourG = [G, G, G, G]
  236. fourpos = [pos_OK, pos_OK, pos_OK, pos_OK]
  237. # input single dict to use for all graphs
  238. to_latex(fourG, pos_OK)
  239. with pytest.raises(nx.NetworkXError):
  240. to_latex(fourG, pos_bad)
  241. # input list of dicts to use for all graphs
  242. to_latex(fourG, fourpos)
  243. with pytest.raises(nx.NetworkXError):
  244. to_latex(fourG, [pos_bad, pos_bad, pos_bad, pos_bad])
  245. # every pos dict must include all nodes
  246. with pytest.raises(nx.NetworkXError):
  247. to_latex(fourG, [pos_OK, pos_OK, pos_bad, pos_OK])
  248. # test sub_captions and sub_labels (len must match Gbunch)
  249. with pytest.raises(nx.NetworkXError):
  250. to_latex(fourG, fourpos, sub_captions=["hi", "hi"])
  251. with pytest.raises(nx.NetworkXError):
  252. to_latex(fourG, fourpos, sub_labels=["hi", "hi"])
  253. # all pass
  254. to_latex(fourG, fourpos, sub_captions=["hi"] * 4, sub_labels=["lbl"] * 4)
  255. def test_exception_multigraph():
  256. G = nx.path_graph(4, create_using=nx.MultiGraph)
  257. G.add_edge(1, 2)
  258. with pytest.raises(nx.NetworkXNotImplemented):
  259. nx.to_latex(G)