Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/drawing/nx_latex.py: 13%
95 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
1r"""
2*****
3LaTeX
4*****
6Export NetworkX graphs in LaTeX format using the TikZ library within TeX/LaTeX.
7Usually, you will want the drawing to appear in a figure environment so
8you use ``to_latex(G, caption="A caption")``. If you want the raw
9drawing commands without a figure environment use :func:`to_latex_raw`.
10And if you want to write to a file instead of just returning the latex
11code as a string, use ``write_latex(G, "filename.tex", caption="A caption")``.
13To construct a figure with subfigures for each graph to be shown, provide
14``to_latex`` or ``write_latex`` a list of graphs, a list of subcaptions,
15and a number of rows of subfigures inside the figure.
17To be able to refer to the figures or subfigures in latex using ``\\ref``,
18the keyword ``latex_label`` is available for figures and `sub_labels` for
19a list of labels, one for each subfigure.
21We intend to eventually provide an interface to the TikZ Graph
22features which include e.g. layout algorithms.
24Let us know via github what you'd like to see available, or better yet
25give us some code to do it, or even better make a github pull request
26to add the feature.
28The TikZ approach
29=================
30Drawing options can be stored on the graph as node/edge attributes, or
31can be provided as dicts keyed by node/edge to a string of the options
32for that node/edge. Similarly a label can be shown for each node/edge
33by specifying the labels as graph node/edge attributes or by providing
34a dict keyed by node/edge to the text to be written for that node/edge.
36Options for the tikzpicture environment (e.g. "[scale=2]") can be provided
37via a keyword argument. Similarly default node and edge options can be
38provided through keywords arguments. The default node options are applied
39to the single TikZ "path" that draws all nodes (and no edges). The default edge
40options are applied to a TikZ "scope" which contains a path for each edge.
42Examples
43========
44>>> G = nx.path_graph(3)
45>>> nx.write_latex(G, "just_my_figure.tex", as_document=True)
46>>> nx.write_latex(G, "my_figure.tex", caption="A path graph", latex_label="fig1")
47>>> latex_code = nx.to_latex(G) # a string rather than a file
49You can change many features of the nodes and edges.
51>>> G = nx.path_graph(4, create_using=nx.DiGraph)
52>>> pos = {n: (n, n) for n in G} # nodes set on a line
54>>> G.nodes[0]["style"] = "blue"
55>>> G.nodes[2]["style"] = "line width=3,draw"
56>>> G.nodes[3]["label"] = "Stop"
57>>> G.edges[(0, 1)]["label"] = "1st Step"
58>>> G.edges[(0, 1)]["label_opts"] = "near start"
59>>> G.edges[(1, 2)]["style"] = "line width=3"
60>>> G.edges[(1, 2)]["label"] = "2nd Step"
61>>> G.edges[(2, 3)]["style"] = "green"
62>>> G.edges[(2, 3)]["label"] = "3rd Step"
63>>> G.edges[(2, 3)]["label_opts"] = "near end"
65>>> nx.write_latex(G, "latex_graph.tex", pos=pos, as_document=True)
67Then compile the LaTeX using something like ``pdflatex latex_graph.tex``
68and view the pdf file created: ``latex_graph.pdf``.
70If you want **subfigures** each containing one graph, you can input a list of graphs.
72>>> H1 = nx.path_graph(4)
73>>> H2 = nx.complete_graph(4)
74>>> H3 = nx.path_graph(8)
75>>> H4 = nx.complete_graph(8)
76>>> graphs = [H1, H2, H3, H4]
77>>> caps = ["Path 4", "Complete graph 4", "Path 8", "Complete graph 8"]
78>>> lbls = ["fig2a", "fig2b", "fig2c", "fig2d"]
79>>> nx.write_latex(graphs, "subfigs.tex", n_rows=2, sub_captions=caps, sub_labels=lbls)
80>>> latex_code = nx.to_latex(graphs, n_rows=2, sub_captions=caps, sub_labels=lbls)
82>>> node_color = {0: "red", 1: "orange", 2: "blue", 3: "gray!90"}
83>>> edge_width = {e: "line width=1.5" for e in H3.edges}
84>>> pos = nx.circular_layout(H3)
85>>> latex_code = nx.to_latex(H3, pos, node_options=node_color, edge_options=edge_width)
86>>> print(latex_code)
87\documentclass{report}
88\usepackage{tikz}
89\usepackage{subcaption}
90<BLANKLINE>
91\begin{document}
92\begin{figure}
93 \begin{tikzpicture}
94 \draw
95 (1.0, 0.0) node[red] (0){0}
96 (0.707, 0.707) node[orange] (1){1}
97 (-0.0, 1.0) node[blue] (2){2}
98 (-0.707, 0.707) node[gray!90] (3){3}
99 (-1.0, -0.0) node (4){4}
100 (-0.707, -0.707) node (5){5}
101 (0.0, -1.0) node (6){6}
102 (0.707, -0.707) node (7){7};
103 \begin{scope}[-]
104 \draw[line width=1.5] (0) to (1);
105 \draw[line width=1.5] (1) to (2);
106 \draw[line width=1.5] (2) to (3);
107 \draw[line width=1.5] (3) to (4);
108 \draw[line width=1.5] (4) to (5);
109 \draw[line width=1.5] (5) to (6);
110 \draw[line width=1.5] (6) to (7);
111 \end{scope}
112 \end{tikzpicture}
113\end{figure}
114\end{document}
116Notes
117-----
118If you want to change the preamble/postamble of the figure/document/subfigure
119environment, use the keyword arguments: `figure_wrapper`, `document_wrapper`,
120`subfigure_wrapper`. The default values are stored in private variables
121e.g. ``nx.nx_layout._DOCUMENT_WRAPPER``
123References
124----------
125TikZ: https://tikz.dev/
127TikZ options details: https://tikz.dev/tikz-actions
128"""
129import numbers
130import os
132import networkx as nx
134__all__ = [
135 "to_latex_raw",
136 "to_latex",
137 "write_latex",
138]
141@nx.utils.not_implemented_for("multigraph")
142def to_latex_raw(
143 G,
144 pos="pos",
145 tikz_options="",
146 default_node_options="",
147 node_options="node_options",
148 node_label="label",
149 default_edge_options="",
150 edge_options="edge_options",
151 edge_label="label",
152 edge_label_options="edge_label_options",
153):
154 """Return a string of the LaTeX/TikZ code to draw `G`
156 This function produces just the code for the tikzpicture
157 without any enclosing environment.
159 Parameters
160 ==========
161 G : NetworkX graph
162 The NetworkX graph to be drawn
163 pos : string or dict (default "pos")
164 The name of the node attribute on `G` that holds the position of each node.
165 Positions can be sequences of length 2 with numbers for (x,y) coordinates.
166 They can also be strings to denote positions in TikZ style, such as (x, y)
167 or (angle:radius).
168 If a dict, it should be keyed by node to a position.
169 If an empty dict, a circular layout is computed by TikZ.
170 tikz_options : string
171 The tikzpicture options description defining the options for the picture.
172 Often large scale options like `[scale=2]`.
173 default_node_options : string
174 The draw options for a path of nodes. Individual node options override these.
175 node_options : string or dict
176 The name of the node attribute on `G` that holds the options for each node.
177 Or a dict keyed by node to a string holding the options for that node.
178 node_label : string or dict
179 The name of the node attribute on `G` that holds the node label (text)
180 displayed for each node. If the attribute is "" or not present, the node
181 itself is drawn as a string. LaTeX processing such as ``"$A_1$"`` is allowed.
182 Or a dict keyed by node to a string holding the label for that node.
183 default_edge_options : string
184 The options for the scope drawing all edges. The default is "[-]" for
185 undirected graphs and "[->]" for directed graphs.
186 edge_options : string or dict
187 The name of the edge attribute on `G` that holds the options for each edge.
188 If the edge is a self-loop and ``"loop" not in edge_options`` the option
189 "loop," is added to the options for the self-loop edge. Hence you can
190 use "[loop above]" explicitly, but the default is "[loop]".
191 Or a dict keyed by edge to a string holding the options for that edge.
192 edge_label : string or dict
193 The name of the edge attribute on `G` that holds the edge label (text)
194 displayed for each edge. If the attribute is "" or not present, no edge
195 label is drawn.
196 Or a dict keyed by edge to a string holding the label for that edge.
197 edge_label_options : string or dict
198 The name of the edge attribute on `G` that holds the label options for
199 each edge. For example, "[sloped,above,blue]". The default is no options.
200 Or a dict keyed by edge to a string holding the label options for that edge.
202 Returns
203 =======
204 latex_code : string
205 The text string which draws the desired graph(s) when compiled by LaTeX.
207 See Also
208 ========
209 to_latex
210 write_latex
211 """
212 i4 = "\n "
213 i8 = "\n "
215 # set up position dict
216 # TODO allow pos to be None and use a nice TikZ default
217 if not isinstance(pos, dict):
218 pos = nx.get_node_attributes(G, pos)
219 if not pos:
220 # circular layout with radius 2
221 pos = {n: f"({round(360.0 * i / len(G), 3)}:2)" for i, n in enumerate(G)}
222 for node in G:
223 if node not in pos:
224 raise nx.NetworkXError(f"node {node} has no specified pos {pos}")
225 posnode = pos[node]
226 if not isinstance(posnode, str):
227 try:
228 posx, posy = posnode
229 pos[node] = f"({round(posx, 3)}, {round(posy, 3)})"
230 except (TypeError, ValueError):
231 msg = f"position pos[{node}] is not 2-tuple or a string: {posnode}"
232 raise nx.NetworkXError(msg)
234 # set up all the dicts
235 if not isinstance(node_options, dict):
236 node_options = nx.get_node_attributes(G, node_options)
237 if not isinstance(node_label, dict):
238 node_label = nx.get_node_attributes(G, node_label)
239 if not isinstance(edge_options, dict):
240 edge_options = nx.get_edge_attributes(G, edge_options)
241 if not isinstance(edge_label, dict):
242 edge_label = nx.get_edge_attributes(G, edge_label)
243 if not isinstance(edge_label_options, dict):
244 edge_label_options = nx.get_edge_attributes(G, edge_label_options)
246 # process default options (add brackets or not)
247 topts = "" if tikz_options == "" else f"[{tikz_options.strip('[]')}]"
248 defn = "" if default_node_options == "" else f"[{default_node_options.strip('[]')}]"
249 linestyle = f"{'->' if G.is_directed() else '-'}"
250 if default_edge_options == "":
251 defe = "[" + linestyle + "]"
252 elif "-" in default_edge_options:
253 defe = default_edge_options
254 else:
255 defe = f"[{linestyle},{default_edge_options.strip('[]')}]"
257 # Construct the string line by line
258 result = " \\begin{tikzpicture}" + topts
259 result += i4 + " \\draw" + defn
260 # load the nodes
261 for n in G:
262 # node options goes inside square brackets
263 nopts = f"[{node_options[n].strip('[]')}]" if n in node_options else ""
264 # node text goes inside curly brackets {}
265 ntext = f"{{{node_label[n]}}}" if n in node_label else f"{{{n}}}"
267 result += i8 + f"{pos[n]} node{nopts} ({n}){ntext}"
268 result += ";\n"
270 # load the edges
271 result += " \\begin{scope}" + defe
272 for edge in G.edges:
273 u, v = edge[:2]
274 e_opts = f"{edge_options[edge]}".strip("[]") if edge in edge_options else ""
275 # add loop options for selfloops if not present
276 if u == v and "loop" not in e_opts:
277 e_opts = "loop," + e_opts
278 e_opts = f"[{e_opts}]" if e_opts != "" else ""
279 # TODO -- handle bending of multiedges
281 els = edge_label_options[edge] if edge in edge_label_options else ""
282 # edge label options goes inside square brackets []
283 els = f"[{els.strip('[]')}]"
284 # edge text is drawn using the TikZ node command inside curly brackets {}
285 e_label = f" node{els} {{{edge_label[edge]}}}" if edge in edge_label else ""
287 result += i8 + f"\\draw{e_opts} ({u}) to{e_label} ({v});"
289 result += "\n \\end{scope}\n \\end{tikzpicture}\n"
290 return result
293_DOC_WRAPPER_TIKZ = r"""\documentclass{{report}}
294\usepackage{{tikz}}
295\usepackage{{subcaption}}
297\begin{{document}}
298{content}
299\end{{document}}"""
302_FIG_WRAPPER = r"""\begin{{figure}}
303{content}{caption}{label}
304\end{{figure}}"""
307_SUBFIG_WRAPPER = r""" \begin{{subfigure}}{{{size}\textwidth}}
308{content}{caption}{label}
309 \end{{subfigure}}"""
312def to_latex(
313 Gbunch,
314 pos="pos",
315 tikz_options="",
316 default_node_options="",
317 node_options="node_options",
318 node_label="node_label",
319 default_edge_options="",
320 edge_options="edge_options",
321 edge_label="edge_label",
322 edge_label_options="edge_label_options",
323 caption="",
324 latex_label="",
325 sub_captions=None,
326 sub_labels=None,
327 n_rows=1,
328 as_document=True,
329 document_wrapper=_DOC_WRAPPER_TIKZ,
330 figure_wrapper=_FIG_WRAPPER,
331 subfigure_wrapper=_SUBFIG_WRAPPER,
332):
333 """Return latex code to draw the graph(s) in `Gbunch`
335 The TikZ drawing utility in LaTeX is used to draw the graph(s).
336 If `Gbunch` is a graph, it is drawn in a figure environment.
337 If `Gbunch` is an iterable of graphs, each is drawn in a subfigure environment
338 within a single figure environment.
340 If `as_document` is True, the figure is wrapped inside a document environment
341 so that the resulting string is ready to be compiled by LaTeX. Otherwise,
342 the string is ready for inclusion in a larger tex document using ``\\include``
343 or ``\\input`` statements.
345 Parameters
346 ==========
347 Gbunch : NetworkX graph or iterable of NetworkX graphs
348 The NetworkX graph to be drawn or an iterable of graphs
349 to be drawn inside subfigures of a single figure.
350 pos : string or list of strings
351 The name of the node attribute on `G` that holds the position of each node.
352 Positions can be sequences of length 2 with numbers for (x,y) coordinates.
353 They can also be strings to denote positions in TikZ style, such as (x, y)
354 or (angle:radius).
355 If a dict, it should be keyed by node to a position.
356 If an empty dict, a circular layout is computed by TikZ.
357 If you are drawing many graphs in subfigures, use a list of position dicts.
358 tikz_options : string
359 The tikzpicture options description defining the options for the picture.
360 Often large scale options like `[scale=2]`.
361 default_node_options : string
362 The draw options for a path of nodes. Individual node options override these.
363 node_options : string or dict
364 The name of the node attribute on `G` that holds the options for each node.
365 Or a dict keyed by node to a string holding the options for that node.
366 node_label : string or dict
367 The name of the node attribute on `G` that holds the node label (text)
368 displayed for each node. If the attribute is "" or not present, the node
369 itself is drawn as a string. LaTeX processing such as ``"$A_1$"`` is allowed.
370 Or a dict keyed by node to a string holding the label for that node.
371 default_edge_options : string
372 The options for the scope drawing all edges. The default is "[-]" for
373 undirected graphs and "[->]" for directed graphs.
374 edge_options : string or dict
375 The name of the edge attribute on `G` that holds the options for each edge.
376 If the edge is a self-loop and ``"loop" not in edge_options`` the option
377 "loop," is added to the options for the self-loop edge. Hence you can
378 use "[loop above]" explicitly, but the default is "[loop]".
379 Or a dict keyed by edge to a string holding the options for that edge.
380 edge_label : string or dict
381 The name of the edge attribute on `G` that holds the edge label (text)
382 displayed for each edge. If the attribute is "" or not present, no edge
383 label is drawn.
384 Or a dict keyed by edge to a string holding the label for that edge.
385 edge_label_options : string or dict
386 The name of the edge attribute on `G` that holds the label options for
387 each edge. For example, "[sloped,above,blue]". The default is no options.
388 Or a dict keyed by edge to a string holding the label options for that edge.
389 caption : string
390 The caption string for the figure environment
391 latex_label : string
392 The latex label used for the figure for easy referral from the main text
393 sub_captions : list of strings
394 The sub_caption string for each subfigure in the figure
395 sub_latex_labels : list of strings
396 The latex label for each subfigure in the figure
397 n_rows : int
398 The number of rows of subfigures to arrange for multiple graphs
399 as_document : bool
400 Whether to wrap the latex code in a document environment for compiling
401 document_wrapper : formatted text string with variable ``content``.
402 This text is called to evaluate the content embedded in a document
403 environment with a preamble setting up TikZ.
404 figure_wrapper : formatted text string
405 This text is evaluated with variables ``content``, ``caption`` and ``label``.
406 It wraps the content and if a caption is provided, adds the latex code for
407 that caption, and if a label is provided, adds the latex code for a label.
408 subfigure_wrapper : formatted text string
409 This text evaluate variables ``size``, ``content``, ``caption`` and ``label``.
410 It wraps the content and if a caption is provided, adds the latex code for
411 that caption, and if a label is provided, adds the latex code for a label.
412 The size is the vertical size of each row of subfigures as a fraction.
414 Returns
415 =======
416 latex_code : string
417 The text string which draws the desired graph(s) when compiled by LaTeX.
419 See Also
420 ========
421 write_latex
422 to_latex_raw
423 """
424 if hasattr(Gbunch, "adj"):
425 raw = to_latex_raw(
426 Gbunch,
427 pos,
428 tikz_options,
429 default_node_options,
430 node_options,
431 node_label,
432 default_edge_options,
433 edge_options,
434 edge_label,
435 edge_label_options,
436 )
437 else: # iterator of graphs
438 sbf = subfigure_wrapper
439 size = 1 / n_rows
441 N = len(Gbunch)
442 if isinstance(pos, (str, dict)):
443 pos = [pos] * N
444 if sub_captions is None:
445 sub_captions = [""] * N
446 if sub_labels is None:
447 sub_labels = [""] * N
448 if not (len(Gbunch) == len(pos) == len(sub_captions) == len(sub_labels)):
449 raise nx.NetworkXError(
450 "length of Gbunch, sub_captions and sub_figures must agree"
451 )
453 raw = ""
454 for G, pos, subcap, sublbl in zip(Gbunch, pos, sub_captions, sub_labels):
455 subraw = to_latex_raw(
456 G,
457 pos,
458 tikz_options,
459 default_node_options,
460 node_options,
461 node_label,
462 default_edge_options,
463 edge_options,
464 edge_label,
465 edge_label_options,
466 )
467 cap = f" \\caption{{{subcap}}}" if subcap else ""
468 lbl = f"\\label{{{sublbl}}}" if sublbl else ""
469 raw += sbf.format(size=size, content=subraw, caption=cap, label=lbl)
470 raw += "\n"
472 # put raw latex code into a figure environment and optionally into a document
473 raw = raw[:-1]
474 cap = f"\n \\caption{{{caption}}}" if caption else ""
475 lbl = f"\\label{{{latex_label}}}" if latex_label else ""
476 fig = figure_wrapper.format(content=raw, caption=cap, label=lbl)
477 if as_document:
478 return document_wrapper.format(content=fig)
479 return fig
482@nx.utils.open_file(1, mode="w")
483def write_latex(Gbunch, path, **options):
484 """Write the latex code to draw the graph(s) onto `path`.
486 This convenience function creates the latex drawing code as a string
487 and writes that to a file ready to be compiled when `as_document` is True
488 or ready to be ``import`` ed or ``include`` ed into your main LaTeX document.
490 The `path` argument can be a string filename or a file handle to write to.
492 Parameters
493 ----------
494 Gbunch : NetworkX graph or iterable of NetworkX graphs
495 If Gbunch is a graph, it is drawn in a figure environment.
496 If Gbunch is an iterable of graphs, each is drawn in a subfigure
497 environment within a single figure environment.
498 path : filename
499 Filename or file handle to write to
500 options : dict
501 By default, TikZ is used with options: (others are ignored)::
503 pos : string or dict or list
504 The name of the node attribute on `G` that holds the position of each node.
505 Positions can be sequences of length 2 with numbers for (x,y) coordinates.
506 They can also be strings to denote positions in TikZ style, such as (x, y)
507 or (angle:radius).
508 If a dict, it should be keyed by node to a position.
509 If an empty dict, a circular layout is computed by TikZ.
510 If you are drawing many graphs in subfigures, use a list of position dicts.
511 tikz_options : string
512 The tikzpicture options description defining the options for the picture.
513 Often large scale options like `[scale=2]`.
514 default_node_options : string
515 The draw options for a path of nodes. Individual node options override these.
516 node_options : string or dict
517 The name of the node attribute on `G` that holds the options for each node.
518 Or a dict keyed by node to a string holding the options for that node.
519 node_label : string or dict
520 The name of the node attribute on `G` that holds the node label (text)
521 displayed for each node. If the attribute is "" or not present, the node
522 itself is drawn as a string. LaTeX processing such as ``"$A_1$"`` is allowed.
523 Or a dict keyed by node to a string holding the label for that node.
524 default_edge_options : string
525 The options for the scope drawing all edges. The default is "[-]" for
526 undirected graphs and "[->]" for directed graphs.
527 edge_options : string or dict
528 The name of the edge attribute on `G` that holds the options for each edge.
529 If the edge is a self-loop and ``"loop" not in edge_options`` the option
530 "loop," is added to the options for the self-loop edge. Hence you can
531 use "[loop above]" explicitly, but the default is "[loop]".
532 Or a dict keyed by edge to a string holding the options for that edge.
533 edge_label : string or dict
534 The name of the edge attribute on `G` that holds the edge label (text)
535 displayed for each edge. If the attribute is "" or not present, no edge
536 label is drawn.
537 Or a dict keyed by edge to a string holding the label for that edge.
538 edge_label_options : string or dict
539 The name of the edge attribute on `G` that holds the label options for
540 each edge. For example, "[sloped,above,blue]". The default is no options.
541 Or a dict keyed by edge to a string holding the label options for that edge.
542 caption : string
543 The caption string for the figure environment
544 latex_label : string
545 The latex label used for the figure for easy referral from the main text
546 sub_captions : list of strings
547 The sub_caption string for each subfigure in the figure
548 sub_latex_labels : list of strings
549 The latex label for each subfigure in the figure
550 n_rows : int
551 The number of rows of subfigures to arrange for multiple graphs
552 as_document : bool
553 Whether to wrap the latex code in a document environment for compiling
554 document_wrapper : formatted text string with variable ``content``.
555 This text is called to evaluate the content embedded in a document
556 environment with a preamble setting up the TikZ syntax.
557 figure_wrapper : formatted text string
558 This text is evaluated with variables ``content``, ``caption`` and ``label``.
559 It wraps the content and if a caption is provided, adds the latex code for
560 that caption, and if a label is provided, adds the latex code for a label.
561 subfigure_wrapper : formatted text string
562 This text evaluate variables ``size``, ``content``, ``caption`` and ``label``.
563 It wraps the content and if a caption is provided, adds the latex code for
564 that caption, and if a label is provided, adds the latex code for a label.
565 The size is the vertical size of each row of subfigures as a fraction.
567 See Also
568 ========
569 to_latex
570 """
571 path.write(to_latex(Gbunch, **options))