| r""" |
| ***** |
| LaTeX |
| ***** |
| |
| Export NetworkX graphs in LaTeX format using the TikZ library within TeX/LaTeX. |
| Usually, you will want the drawing to appear in a figure environment so |
| you use ``to_latex(G, caption="A caption")``. If you want the raw |
| drawing commands without a figure environment use :func:`to_latex_raw`. |
| And if you want to write to a file instead of just returning the latex |
| code as a string, use ``write_latex(G, "filname.tex", caption="A caption")``. |
| |
| To construct a figure with subfigures for each graph to be shown, provide |
| ``to_latex`` or ``write_latex`` a list of graphs, a list of subcaptions, |
| and a number of rows of subfigures inside the figure. |
| |
| To be able to refer to the figures or subfigures in latex using ``\\ref``, |
| the keyword ``latex_label`` is available for figures and `sub_labels` for |
| a list of labels, one for each subfigure. |
| |
| We intend to eventually provide an interface to the TikZ Graph |
| features which include e.g. layout algorithms. |
| |
| Let us know via github what you'd like to see available, or better yet |
| give us some code to do it, or even better make a github pull request |
| to add the feature. |
| |
| The TikZ approach |
| ================= |
| Drawing options can be stored on the graph as node/edge attributes, or |
| can be provided as dicts keyed by node/edge to a string of the options |
| for that node/edge. Similarly a label can be shown for each node/edge |
| by specifying the labels as graph node/edge attributes or by providing |
| a dict keyed by node/edge to the text to be written for that node/edge. |
| |
| Options for the tikzpicture environment (e.g. "[scale=2]") can be provided |
| via a keyword argument. Similarly default node and edge options can be |
| provided through keywords arguments. The default node options are applied |
| to the single TikZ "path" that draws all nodes (and no edges). The default edge |
| options are applied to a TikZ "scope" which contains a path for each edge. |
| |
| Examples |
| ======== |
| >>> G = nx.path_graph(3) |
| >>> nx.write_latex(G, "just_my_figure.tex", as_document=True) |
| >>> nx.write_latex(G, "my_figure.tex", caption="A path graph", latex_label="fig1") |
| >>> latex_code = nx.to_latex(G) # a string rather than a file |
| |
| You can change many features of the nodes and edges. |
| |
| >>> G = nx.path_graph(4, create_using=nx.DiGraph) |
| >>> pos = {n: (n, n) for n in G} # nodes set on a line |
| |
| >>> G.nodes[0]["style"] = "blue" |
| >>> G.nodes[2]["style"] = "line width=3,draw" |
| >>> G.nodes[3]["label"] = "Stop" |
| >>> G.edges[(0, 1)]["label"] = "1st Step" |
| >>> G.edges[(0, 1)]["label_opts"] = "near start" |
| >>> G.edges[(1, 2)]["style"] = "line width=3" |
| >>> G.edges[(1, 2)]["label"] = "2nd Step" |
| >>> G.edges[(2, 3)]["style"] = "green" |
| >>> G.edges[(2, 3)]["label"] = "3rd Step" |
| >>> G.edges[(2, 3)]["label_opts"] = "near end" |
| |
| >>> nx.write_latex(G, "latex_graph.tex", pos=pos, as_document=True) |
| |
| Then compile the LaTeX using something like ``pdflatex latex_graph.tex`` |
| and view the pdf file created: ``latex_graph.pdf``. |
| |
| If you want **subfigures** each containing one graph, you can input a list of graphs. |
| |
| >>> H1 = nx.path_graph(4) |
| >>> H2 = nx.complete_graph(4) |
| >>> H3 = nx.path_graph(8) |
| >>> H4 = nx.complete_graph(8) |
| >>> graphs = [H1, H2, H3, H4] |
| >>> caps = ["Path 4", "Complete graph 4", "Path 8", "Complete graph 8"] |
| >>> lbls = ["fig2a", "fig2b", "fig2c", "fig2d"] |
| >>> nx.write_latex(graphs, "subfigs.tex", n_rows=2, sub_captions=caps, sub_labels=lbls) |
| >>> latex_code = nx.to_latex(graphs, n_rows=2, sub_captions=caps, sub_labels=lbls) |
| |
| >>> node_color = {0: "red", 1: "orange", 2: "blue", 3: "gray!90"} |
| >>> edge_width = {e: "line width=1.5" for e in H3.edges} |
| >>> pos = nx.circular_layout(H3) |
| >>> latex_code = nx.to_latex(H3, pos, node_options=node_color, edge_options=edge_width) |
| >>> print(latex_code) |
| \documentclass{report} |
| \usepackage{tikz} |
| \usepackage{subcaption} |
| <BLANKLINE> |
| \begin{document} |
| \begin{figure} |
| \begin{tikzpicture} |
| \draw |
| (1.0, 0.0) node[red] (0){0} |
| (0.707, 0.707) node[orange] (1){1} |
| (-0.0, 1.0) node[blue] (2){2} |
| (-0.707, 0.707) node[gray!90] (3){3} |
| (-1.0, -0.0) node (4){4} |
| (-0.707, -0.707) node (5){5} |
| (0.0, -1.0) node (6){6} |
| (0.707, -0.707) node (7){7}; |
| \begin{scope}[-] |
| \draw[line width=1.5] (0) to (1); |
| \draw[line width=1.5] (1) to (2); |
| \draw[line width=1.5] (2) to (3); |
| \draw[line width=1.5] (3) to (4); |
| \draw[line width=1.5] (4) to (5); |
| \draw[line width=1.5] (5) to (6); |
| \draw[line width=1.5] (6) to (7); |
| \end{scope} |
| \end{tikzpicture} |
| \end{figure} |
| \end{document} |
| |
| Notes |
| ----- |
| If you want to change the preamble/postamble of the figure/document/subfigure |
| environment, use the keyword arguments: `figure_wrapper`, `document_wrapper`, |
| `subfigure_wrapper`. The default values are stored in private variables |
| e.g. ``nx.nx_layout._DOCUMENT_WRAPPER`` |
| |
| References |
| ---------- |
| TikZ: https://tikz.dev/ |
| |
| TikZ options details: https://tikz.dev/tikz-actions |
| """ |
| import numbers |
| import os |
|
|
| import networkx as nx |
|
|
| __all__ = [ |
| "to_latex_raw", |
| "to_latex", |
| "write_latex", |
| ] |
|
|
|
|
| @nx.utils.not_implemented_for("multigraph") |
| def to_latex_raw( |
| G, |
| pos="pos", |
| tikz_options="", |
| default_node_options="", |
| node_options="node_options", |
| node_label="label", |
| default_edge_options="", |
| edge_options="edge_options", |
| edge_label="label", |
| edge_label_options="edge_label_options", |
| ): |
| """Return a string of the LaTeX/TikZ code to draw `G` |
| |
| This function produces just the code for the tikzpicture |
| without any enclosing environment. |
| |
| Parameters |
| ========== |
| G : NetworkX graph |
| The NetworkX graph to be drawn |
| pos : string or dict (default "pos") |
| The name of the node attribute on `G` that holds the position of each node. |
| Positions can be sequences of length 2 with numbers for (x,y) coordinates. |
| They can also be strings to denote positions in TikZ style, such as (x, y) |
| or (angle:radius). |
| If a dict, it should be keyed by node to a position. |
| If an empty dict, a circular layout is computed by TikZ. |
| tikz_options : string |
| The tikzpicture options description defining the options for the picture. |
| Often large scale options like `[scale=2]`. |
| default_node_options : string |
| The draw options for a path of nodes. Individual node options override these. |
| node_options : string or dict |
| The name of the node attribute on `G` that holds the options for each node. |
| Or a dict keyed by node to a string holding the options for that node. |
| node_label : string or dict |
| The name of the node attribute on `G` that holds the node label (text) |
| displayed for each node. If the attribute is "" or not present, the node |
| itself is drawn as a string. LaTeX processing such as ``"$A_1$"`` is allowed. |
| Or a dict keyed by node to a string holding the label for that node. |
| default_edge_options : string |
| The options for the scope drawing all edges. The default is "[-]" for |
| undirected graphs and "[->]" for directed graphs. |
| edge_options : string or dict |
| The name of the edge attribute on `G` that holds the options for each edge. |
| If the edge is a self-loop and ``"loop" not in edge_options`` the option |
| "loop," is added to the options for the self-loop edge. Hence you can |
| use "[loop above]" explicitly, but the default is "[loop]". |
| Or a dict keyed by edge to a string holding the options for that edge. |
| edge_label : string or dict |
| The name of the edge attribute on `G` that holds the edge label (text) |
| displayed for each edge. If the attribute is "" or not present, no edge |
| label is drawn. |
| Or a dict keyed by edge to a string holding the label for that edge. |
| edge_label_options : string or dict |
| The name of the edge attribute on `G` that holds the label options for |
| each edge. For example, "[sloped,above,blue]". The default is no options. |
| Or a dict keyed by edge to a string holding the label options for that edge. |
| |
| Returns |
| ======= |
| latex_code : string |
| The text string which draws the desired graph(s) when compiled by LaTeX. |
| |
| See Also |
| ======== |
| to_latex |
| write_latex |
| """ |
| i4 = "\n " |
| i8 = "\n " |
|
|
| |
| |
| if not isinstance(pos, dict): |
| pos = nx.get_node_attributes(G, pos) |
| if not pos: |
| |
| pos = {n: f"({round(2* 3.1415 * i / len(G), 3)}:10)" for i, n in enumerate(G)} |
| for node in G: |
| if node not in pos: |
| raise nx.NetworkXError(f"node {node} has no specified pos {pos}") |
| posnode = pos[node] |
| if not isinstance(posnode, str): |
| try: |
| posx, posy = posnode |
| pos[node] = f"({round(posx, 3)}, {round(posy, 3)})" |
| except (TypeError, ValueError): |
| msg = f"position pos[{node}] is not 2-tuple or a string: {posnode}" |
| raise nx.NetworkXError(msg) |
|
|
| |
| if not isinstance(node_options, dict): |
| node_options = nx.get_node_attributes(G, node_options) |
| if not isinstance(node_label, dict): |
| node_label = nx.get_node_attributes(G, node_label) |
| if not isinstance(edge_options, dict): |
| edge_options = nx.get_edge_attributes(G, edge_options) |
| if not isinstance(edge_label, dict): |
| edge_label = nx.get_edge_attributes(G, edge_label) |
| if not isinstance(edge_label_options, dict): |
| edge_label_options = nx.get_edge_attributes(G, edge_label_options) |
|
|
| |
| topts = "" if tikz_options == "" else f"[{tikz_options.strip('[]')}]" |
| defn = "" if default_node_options == "" else f"[{default_node_options.strip('[]')}]" |
| linestyle = f"{'->' if G.is_directed() else '-'}" |
| if default_edge_options == "": |
| defe = "[" + linestyle + "]" |
| elif "-" in default_edge_options: |
| defe = default_edge_options |
| else: |
| defe = f"[{linestyle},{default_edge_options.strip('[]')}]" |
|
|
| |
| result = " \\begin{tikzpicture}" + topts |
| result += i4 + " \\draw" + defn |
| |
| for n in G: |
| |
| nopts = f"[{node_options[n].strip('[]')}]" if n in node_options else "" |
| |
| ntext = f"{{{node_label[n]}}}" if n in node_label else f"{{{n}}}" |
|
|
| result += i8 + f"{pos[n]} node{nopts} ({n}){ntext}" |
| result += ";\n" |
|
|
| |
| result += " \\begin{scope}" + defe |
| for edge in G.edges: |
| u, v = edge[:2] |
| e_opts = f"{edge_options[edge]}".strip("[]") if edge in edge_options else "" |
| |
| if u == v and "loop" not in e_opts: |
| e_opts = "loop," + e_opts |
| e_opts = f"[{e_opts}]" if e_opts != "" else "" |
| |
|
|
| els = edge_label_options[edge] if edge in edge_label_options else "" |
| |
| els = f"[{els.strip('[]')}]" |
| |
| e_label = f" node{els} {{{edge_label[edge]}}}" if edge in edge_label else "" |
|
|
| result += i8 + f"\\draw{e_opts} ({u}) to{e_label} ({v});" |
|
|
| result += "\n \\end{scope}\n \\end{tikzpicture}\n" |
| return result |
|
|
|
|
| _DOC_WRAPPER_TIKZ = r"""\documentclass{{report}} |
| \usepackage{{tikz}} |
| \usepackage{{subcaption}} |
| |
| \begin{{document}} |
| {content} |
| \end{{document}}""" |
|
|
|
|
| _FIG_WRAPPER = r"""\begin{{figure}} |
| {content}{caption}{label} |
| \end{{figure}}""" |
|
|
|
|
| _SUBFIG_WRAPPER = r""" \begin{{subfigure}}{{{size}\textwidth}} |
| {content}{caption}{label} |
| \end{{subfigure}}""" |
|
|
|
|
| def to_latex( |
| Gbunch, |
| pos="pos", |
| tikz_options="", |
| default_node_options="", |
| node_options="node_options", |
| node_label="node_label", |
| default_edge_options="", |
| edge_options="edge_options", |
| edge_label="edge_label", |
| edge_label_options="edge_label_options", |
| caption="", |
| latex_label="", |
| sub_captions=None, |
| sub_labels=None, |
| n_rows=1, |
| as_document=True, |
| document_wrapper=_DOC_WRAPPER_TIKZ, |
| figure_wrapper=_FIG_WRAPPER, |
| subfigure_wrapper=_SUBFIG_WRAPPER, |
| ): |
| """Return latex code to draw the graph(s) in `Gbunch` |
| |
| The TikZ drawing utility in LaTeX is used to draw the graph(s). |
| If `Gbunch` is a graph, it is drawn in a figure environment. |
| If `Gbunch` is an iterable of graphs, each is drawn in a subfigure envionment |
| within a single figure environment. |
| |
| If `as_document` is True, the figure is wrapped inside a document environment |
| so that the resulting string is ready to be compiled by LaTeX. Otherwise, |
| the string is ready for inclusion in a larger tex document using ``\\include`` |
| or ``\\input`` statements. |
| |
| Parameters |
| ========== |
| Gbunch : NetworkX graph or iterable of NetworkX graphs |
| The NetworkX graph to be drawn or an iterable of graphs |
| to be drawn inside subfigures of a single figure. |
| pos : string or list of strings |
| The name of the node attribute on `G` that holds the position of each node. |
| Positions can be sequences of length 2 with numbers for (x,y) coordinates. |
| They can also be strings to denote positions in TikZ style, such as (x, y) |
| or (angle:radius). |
| If a dict, it should be keyed by node to a position. |
| If an empty dict, a circular layout is computed by TikZ. |
| If you are drawing many graphs in subfigures, use a list of position dicts. |
| tikz_options : string |
| The tikzpicture options description defining the options for the picture. |
| Often large scale options like `[scale=2]`. |
| default_node_options : string |
| The draw options for a path of nodes. Individual node options override these. |
| node_options : string or dict |
| The name of the node attribute on `G` that holds the options for each node. |
| Or a dict keyed by node to a string holding the options for that node. |
| node_label : string or dict |
| The name of the node attribute on `G` that holds the node label (text) |
| displayed for each node. If the attribute is "" or not present, the node |
| itself is drawn as a string. LaTeX processing such as ``"$A_1$"`` is allowed. |
| Or a dict keyed by node to a string holding the label for that node. |
| default_edge_options : string |
| The options for the scope drawing all edges. The default is "[-]" for |
| undirected graphs and "[->]" for directed graphs. |
| edge_options : string or dict |
| The name of the edge attribute on `G` that holds the options for each edge. |
| If the edge is a self-loop and ``"loop" not in edge_options`` the option |
| "loop," is added to the options for the self-loop edge. Hence you can |
| use "[loop above]" explicitly, but the default is "[loop]". |
| Or a dict keyed by edge to a string holding the options for that edge. |
| edge_label : string or dict |
| The name of the edge attribute on `G` that holds the edge label (text) |
| displayed for each edge. If the attribute is "" or not present, no edge |
| label is drawn. |
| Or a dict keyed by edge to a string holding the label for that edge. |
| edge_label_options : string or dict |
| The name of the edge attribute on `G` that holds the label options for |
| each edge. For example, "[sloped,above,blue]". The default is no options. |
| Or a dict keyed by edge to a string holding the label options for that edge. |
| caption : string |
| The caption string for the figure environment |
| latex_label : string |
| The latex label used for the figure for easy referral from the main text |
| sub_captions : list of strings |
| The sub_caption string for each subfigure in the figure |
| sub_latex_labels : list of strings |
| The latex label for each subfigure in the figure |
| n_rows : int |
| The number of rows of subfigures to arrange for multiple graphs |
| as_document : bool |
| Whether to wrap the latex code in a document envionment for compiling |
| document_wrapper : formatted text string with variable ``content``. |
| This text is called to evaluate the content embedded in a document |
| environment with a preamble setting up TikZ. |
| figure_wrapper : formatted text string |
| This text is evaluated with variables ``content``, ``caption`` and ``label``. |
| It wraps the content and if a caption is provided, adds the latex code for |
| that caption, and if a label is provided, adds the latex code for a label. |
| subfigure_wrapper : formatted text string |
| This text evaluate variables ``size``, ``content``, ``caption`` and ``label``. |
| It wraps the content and if a caption is provided, adds the latex code for |
| that caption, and if a label is provided, adds the latex code for a label. |
| The size is the vertical size of each row of subfigures as a fraction. |
| |
| Returns |
| ======= |
| latex_code : string |
| The text string which draws the desired graph(s) when compiled by LaTeX. |
| |
| See Also |
| ======== |
| write_latex |
| to_latex_raw |
| """ |
| if hasattr(Gbunch, "adj"): |
| raw = to_latex_raw( |
| Gbunch, |
| pos, |
| tikz_options, |
| default_node_options, |
| node_options, |
| node_label, |
| default_edge_options, |
| edge_options, |
| edge_label, |
| edge_label_options, |
| ) |
| else: |
| sbf = subfigure_wrapper |
| size = 1 / n_rows |
|
|
| N = len(Gbunch) |
| if isinstance(pos, (str, dict)): |
| pos = [pos] * N |
| if sub_captions is None: |
| sub_captions = [""] * N |
| if sub_labels is None: |
| sub_labels = [""] * N |
| if not (len(Gbunch) == len(pos) == len(sub_captions) == len(sub_labels)): |
| raise nx.NetworkXError( |
| "length of Gbunch, sub_captions and sub_figures must agree" |
| ) |
|
|
| raw = "" |
| for G, pos, subcap, sublbl in zip(Gbunch, pos, sub_captions, sub_labels): |
| subraw = to_latex_raw( |
| G, |
| pos, |
| tikz_options, |
| default_node_options, |
| node_options, |
| node_label, |
| default_edge_options, |
| edge_options, |
| edge_label, |
| edge_label_options, |
| ) |
| cap = f" \\caption{{{subcap}}}" if subcap else "" |
| lbl = f"\\label{{{sublbl}}}" if sublbl else "" |
| raw += sbf.format(size=size, content=subraw, caption=cap, label=lbl) |
| raw += "\n" |
|
|
| |
| raw = raw[:-1] |
| cap = f"\n \\caption{{{caption}}}" if caption else "" |
| lbl = f"\\label{{{latex_label}}}" if latex_label else "" |
| fig = figure_wrapper.format(content=raw, caption=cap, label=lbl) |
| if as_document: |
| return document_wrapper.format(content=fig) |
| return fig |
|
|
|
|
| @nx.utils.open_file(1, mode="w") |
| def write_latex(Gbunch, path, **options): |
| """Write the latex code to draw the graph(s) onto `path`. |
| |
| This convenience function creates the latex drawing code as a string |
| and writes that to a file ready to be compiled when `as_document` is True |
| or ready to be ``\\import``ed or `\\include``ed into your main LaTeX document. |
| |
| The `path` argument can be a string filename or a file handle to write to. |
| |
| Parameters |
| ---------- |
| Gbunch : NetworkX graph or iterable of NetworkX graphs |
| If Gbunch is a graph, it is drawn in a figure environment. |
| If Gbunch is an iterable of graphs, each is drawn in a subfigure |
| envionment within a single figure environment. |
| path : filename |
| Filename or file handle to write to |
| options : dict |
| By default, TikZ is used with options: (others are ignored): |
| |
| pos : string or dict or list |
| The name of the node attribute on `G` that holds the position of each node. |
| Positions can be sequences of length 2 with numbers for (x,y) coordinates. |
| They can also be strings to denote positions in TikZ style, such as (x, y) |
| or (angle:radius). |
| If a dict, it should be keyed by node to a position. |
| If an empty dict, a circular layout is computed by TikZ. |
| If you are drawing many graphs in subfigures, use a list of position dicts. |
| tikz_options : string |
| The tikzpicture options description defining the options for the picture. |
| Often large scale options like `[scale=2]`. |
| default_node_options : string |
| The draw options for a path of nodes. Individual node options override these. |
| node_options : string or dict |
| The name of the node attribute on `G` that holds the options for each node. |
| Or a dict keyed by node to a string holding the options for that node. |
| node_label : string or dict |
| The name of the node attribute on `G` that holds the node label (text) |
| displayed for each node. If the attribute is "" or not present, the node |
| itself is drawn as a string. LaTeX processing such as ``"$A_1$"`` is allowed. |
| Or a dict keyed by node to a string holding the label for that node. |
| default_edge_options : string |
| The options for the scope drawing all edges. The default is "[-]" for |
| undirected graphs and "[->]" for directed graphs. |
| edge_options : string or dict |
| The name of the edge attribute on `G` that holds the options for each edge. |
| If the edge is a self-loop and ``"loop" not in edge_options`` the option |
| "loop," is added to the options for the self-loop edge. Hence you can |
| use "[loop above]" explicitly, but the default is "[loop]". |
| Or a dict keyed by edge to a string holding the options for that edge. |
| edge_label : string or dict |
| The name of the edge attribute on `G` that holds the edge label (text) |
| displayed for each edge. If the attribute is "" or not present, no edge |
| label is drawn. |
| Or a dict keyed by edge to a string holding the label for that edge. |
| edge_label_options : string or dict |
| The name of the edge attribute on `G` that holds the label options for |
| each edge. For example, "[sloped,above,blue]". The default is no options. |
| Or a dict keyed by edge to a string holding the label options for that edge. |
| caption : string |
| The caption string for the figure environment |
| latex_label : string |
| The latex label used for the figure for easy referral from the main text |
| sub_captions : list of strings |
| The sub_caption string for each subfigure in the figure |
| sub_latex_labels : list of strings |
| The latex label for each subfigure in the figure |
| n_rows : int |
| The number of rows of subfigures to arrange for multiple graphs |
| as_document : bool |
| Whether to wrap the latex code in a document envionment for compiling |
| document_wrapper : formatted text string with variable ``content``. |
| This text is called to evaluate the content embedded in a document |
| environment with a preamble setting up the TikZ syntax. |
| figure_wrapper : formatted text string |
| This text is evaluated with variables ``content``, ``caption`` and ``label``. |
| It wraps the content and if a caption is provided, adds the latex code for |
| that caption, and if a label is provided, adds the latex code for a label. |
| subfigure_wrapper : formatted text string |
| This text evaluate variables ``size``, ``content``, ``caption`` and ``label``. |
| It wraps the content and if a caption is provided, adds the latex code for |
| that caption, and if a label is provided, adds the latex code for a label. |
| The size is the vertical size of each row of subfigures as a fraction. |
| |
| See Also |
| ======== |
| to_latex |
| """ |
| path.write(to_latex(Gbunch, **options)) |
|
|