def to_networkx(
    g,
    node_attrs=None,
    edge_attrs=None,
    ntype_attr="ntype",
    etype_attr="etype",
    eid_attr="id",
):
    """Convert a graph to a NetworkX graph and return.

    The resulting NetworkX graph also contains the node/edge features of the input graph.
    Additionally, DGL saves the edge IDs as the ``'id'`` edge attribute in the
    returned NetworkX graph.

    Parameters
    ----------
    g : DGLGraph
        A homogeneous or heterogeneous graph.
    node_attrs : iterable of str, optional
        The node attributes to copy from ``g.ndata``. (Default: None)
    edge_attrs : iterable of str, optional
        The edge attributes to copy from ``g.edata``.
        (Default: None)
    ntype_attr : str, optional
        The name of the node attribute to store the node types in the NetworkX object.
        (Default: "ntype")
    etype_attr : str, optional
        The name of the edge attribute to store the edge canonical types in the NetworkX object.
        (Default: "etype")
    eid_attr : str, optional
        The name of the edge attribute to store the original edge ID in the NetworkX object.
        (Default: "id")

    Returns
    -------
    networkx.DiGraph
        The converted NetworkX graph.

    Notes
    -----
    The function only supports CPU graph input.

    Examples
    --------
    The following examples use the PyTorch backend.

    >>> import dgl
    >>> import torch

    With a homogeneous graph:

    >>> g = dgl.graph((torch.tensor([1, 2]), torch.tensor([1, 3])))
    >>> g.ndata['h'] = torch.zeros(4, 1)
    >>> g.edata['h1'] = torch.ones(2, 1)
    >>> g.edata['h2'] = torch.zeros(2, 2)
    >>> nx_g = dgl.to_networkx(g, node_attrs=['h'], edge_attrs=['h1', 'h2'])
    >>> nx_g.nodes(data=True)
    NodeDataView({
        0: {'h': tensor([0.])},
        1: {'h': tensor([0.])},
        2: {'h': tensor([0.])},
        3: {'h': tensor([0.])}
    })
    >>> nx_g.edges(data=True)
    OutMultiEdgeDataView([
        (1, 1, {'id': 0, 'h1': tensor([1.]), 'h2': tensor([0., 0.])}),
        (2, 3, {'id': 1, 'h1': tensor([1.]), 'h2': tensor([0., 0.])})
    ])

    With a heterogeneous graph:

    >>> g = dgl.heterograph({
    ...     ('user', 'follows', 'user'): (torch.tensor([0, 1]), torch.tensor([1, 2])),
    ...     ('user', 'follows', 'topic'): (torch.tensor([1, 1]), torch.tensor([1, 2])),
    ...     ('user', 'plays', 'game'): (torch.tensor([0, 3]), torch.tensor([3, 4]))
    ... })
    >>> g.ndata['n'] = {
    ...     'game': torch.zeros(5, 1),
    ...     'user': torch.ones(4, 1)
    ... }
    >>> g.edata['e'] = {
    ...     ('user', 'follows', 'user'): torch.zeros(2, 1),
    ...     'plays': torch.ones(2, 1)
    ... }
    >>> nx_g = dgl.to_networkx(g, node_attrs=['n'], edge_attrs=['e'])
    >>> nx_g.nodes(data=True)
    NodeDataView({
        0: {'ntype': 'game', 'n': tensor([0.])},
        1: {'ntype': 'game', 'n': tensor([0.])},
        2: {'ntype': 'game', 'n': tensor([0.])},
        3: {'ntype': 'game', 'n': tensor([0.])},
        4: {'ntype': 'game', 'n': tensor([0.])},
        5: {'ntype': 'topic'},
        6: {'ntype': 'topic'},
        7: {'ntype': 'topic'},
        8: {'ntype': 'user', 'n': tensor([1.])},
        9: {'ntype': 'user', 'n': tensor([1.])},
        10: {'ntype': 'user', 'n': tensor([1.])},
        11: {'ntype': 'user', 'n': tensor([1.])}
    })
    >>> nx_g.edges(data=True)
    OutMultiEdgeDataView([
        (8, 9, {'id': 2, 'etype': ('user', 'follows', 'user'), 'e': tensor([0.])}),
        (8, 3, {'id': 4, 'etype': ('user', 'plays', 'game'), 'e': tensor([1.])}),
        (9, 6, {'id': 0, 'etype': ('user', 'follows', 'topic')}),
        (9, 7, {'id': 1, 'etype': ('user', 'follows', 'topic')}),
        (9, 10, {'id': 3, 'etype': ('user', 'follows', 'user'), 'e': tensor([0.])}),
        (11, 4, {'id': 5, 'etype': ('user', 'plays', 'game'), 'e': tensor([1.])})
    ])
    """
    if g.device != F.cpu():
        raise DGLError(
            "Cannot convert a CUDA graph to networkx. Call g.cpu() first."
        )
    if g.is_homogeneous:
        return _to_networkx_homogeneous(g, node_attrs, edge_attrs)
    else:
        return _to_networkx_heterogeneous(
            g, node_attrs, edge_attrs, ntype_attr, etype_attr, eid_attr
        )
Convert a graph to a NetworkX graph and return.

    The resulting NetworkX graph also contains the node/edge features of the input graph.
    Additionally, DGL saves the edge IDs as the ``'id'`` edge attribute in the
    returned NetworkX graph.

    Parameters
    ----------
    g : DGLGraph
        A homogeneous or heterogeneous graph.
    node_attrs : iterable of str, optional
        The node attributes to copy from ``g.ndata``. (Default: None)
    edge_attrs : iterable of str, optional
        The edge attributes to copy from ``g.edata``.
        (Default: None)
    ntype_attr : str, optional
        The name of the node attribute to store the node types in the NetworkX object.
        (Default: "ntype")
    etype_attr : str, optional
        The name of the edge attribute to store the edge canonical types in the NetworkX object.
        (Default: "etype")
    eid_attr : str, optional
        The name of the edge attribute to store the original edge ID in the NetworkX object.
        (Default: "id")

    Returns
    -------
    networkx.DiGraph
        The converted NetworkX graph.

    Notes
    -----
    The function only supports CPU graph input.

    Examples
    --------
    The following examples use the PyTorch backend.

    >>> import dgl
    >>> import torch

    With a homogeneous graph:

    >>> g = dgl.graph((torch.tensor([1, 2]), torch.tensor([1, 3])))
    >>> g.ndata['h'] = torch.zeros(4, 1)
    >>> g.edata['h1'] = torch.ones(2, 1)
    >>> g.edata['h2'] = torch.zeros(2, 2)
    >>> nx_g = dgl.to_networkx(g, node_attrs=['h'], edge_attrs=['h1', 'h2'])
    >>> nx_g.nodes(data=True)
    NodeDataView({
        0: {'h': tensor([0.])},
        1: {'h': tensor([0.])},
        2: {'h': tensor([0.])},
        3: {'h': tensor([0.])}
    })
    >>> nx_g.edges(data=True)
    OutMultiEdgeDataView([
        (1, 1, {'id': 0, 'h1': tensor([1.]), 'h2': tensor([0., 0.])}),
        (2, 3, {'id': 1, 'h1': tensor([1.]), 'h2': tensor([0., 0.])})
    ])

    With a heterogeneous graph:

    >>> g = dgl.heterograph({
    ...     ('user', 'follows', 'user'): (torch.tensor([0, 1]), torch.tensor([1, 2])),
    ...     ('user', 'follows', 'topic'): (torch.tensor([1, 1]), torch.tensor([1, 2])),
    ...     ('user', 'plays', 'game'): (torch.tensor([0, 3]), torch.tensor([3, 4]))
    ... })
    >>> g.ndata['n'] = {
    ...     'game': torch.zeros(5, 1),
    ...     'user': torch.ones(4, 1)
    ... }
    >>> g.edata['e'] = {
    ...     ('user', 'follows', 'user'): torch.zeros(2, 1),
    ...     'plays': torch.ones(2, 1)
    ... }
    >>> nx_g = dgl.to_networkx(g, node_attrs=['n'], edge_attrs=['e'])
    >>> nx_g.nodes(data=True)
    NodeDataView({
        0: {'ntype': 'game', 'n': tensor([0.])},
        1: {'ntype': 'game', 'n': tensor([0.])},
        2: {'ntype': 'game', 'n': tensor([0.])},
        3: {'ntype': 'game', 'n': tensor([0.])},
        4: {'ntype': 'game', 'n': tensor([0.])},
        5: {'ntype': 'topic'},
        6: {'ntype': 'topic'},
        7: {'ntype': 'topic'},
        8: {'ntype': 'user', 'n': tensor([1.])},
        9: {'ntype': 'user', 'n': tensor([1.])},
        10: {'ntype': 'user', 'n': tensor([1.])},
        11: {'ntype': 'user', 'n': tensor([1.])}
    })
    >>> nx_g.edges(data=True)
    OutMultiEdgeDataView([
        (8, 9, {'id': 2, 'etype': ('user', 'follows', 'user'), 'e': tensor([0.])}),
        (8, 3, {'id': 4, 'etype': ('user', 'plays', 'game'), 'e': tensor([1.])}),
        (9, 6, {'id': 0, 'etype': ('user', 'follows', 'topic')}),
        (9, 7, {'id': 1, 'etype': ('user', 'follows', 'topic')}),
        (9, 10, {'id': 3, 'etype': ('user', 'follows', 'user'), 'e': tensor([0.])}),
        (11, 4, {'id': 5, 'etype': ('user', 'plays', 'game'), 'e': tensor([1.])})
    ])
    
python/dgl/convert.py