about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/networkx/drawing/nx_latex.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/networkx/drawing/nx_latex.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/drawing/nx_latex.py')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/drawing/nx_latex.py572
1 files changed, 572 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/drawing/nx_latex.py b/.venv/lib/python3.12/site-packages/networkx/drawing/nx_latex.py
new file mode 100644
index 00000000..5fdbf78b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/drawing/nx_latex.py
@@ -0,0 +1,572 @@
+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, "filename.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        "
+
+    # set up position dict
+    # TODO allow pos to be None and use a nice TikZ default
+    if not isinstance(pos, dict):
+        pos = nx.get_node_attributes(G, pos)
+    if not pos:
+        # circular layout with radius 2
+        pos = {n: f"({round(360.0 * i / len(G), 3)}:2)" 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)
+
+    # set up all the dicts
+    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)
+
+    # process default options (add brackets or not)
+    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('[]')}]"
+
+    # Construct the string line by line
+    result = "  \\begin{tikzpicture}" + topts
+    result += i4 + "  \\draw" + defn
+    # load the nodes
+    for n in G:
+        # node options goes inside square brackets
+        nopts = f"[{node_options[n].strip('[]')}]" if n in node_options else ""
+        # node text goes inside curly brackets {}
+        ntext = f"{{{node_label[n]}}}" if n in node_label else f"{{{n}}}"
+
+        result += i8 + f"{pos[n]} node{nopts} ({n}){ntext}"
+    result += ";\n"
+
+    # load the edges
+    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 ""
+        # add loop options for selfloops if not present
+        if u == v and "loop" not in e_opts:
+            e_opts = "loop," + e_opts
+        e_opts = f"[{e_opts}]" if e_opts != "" else ""
+        # TODO -- handle bending of multiedges
+
+        els = edge_label_options[edge] if edge in edge_label_options else ""
+        # edge label options goes inside square brackets []
+        els = f"[{els.strip('[]')}]"
+        # edge text is drawn using the TikZ node command inside curly brackets {}
+        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 environment
+    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 environment 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:  # iterator of graphs
+        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"
+
+    # put raw latex code into a figure environment and optionally into a document
+    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
+        environment 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 environment 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))