dmlc--dgl
f4f9abc871
* Update from master (#4584)
* [Example][Refactor] Refactor graphsage multigpu and full-graph example (#4430)
* Add refactors for multi-gpu and full-graph example
* Fix format
* Update
* Update
* Update
* [Cleanup] Remove async_transferer (#4505)
* Remove async_transferer
* remove test
* Remove AsyncTransferer
Co-authored-by: Xin Yao <xiny@nvidia.com>
Co-authored-by: Xin Yao <yaox12@outlook.com>
* [Cleanup] Remove duplicate entries of CUB submodule (issue# 4395) (#4499)
* remove third_part/cub
* remove from third_party
Co-authored-by: Israt Nisa <nisisrat@amazon.com>
Co-authored-by: Xin Yao <xiny@nvidia.com>
* [Bug] Enable turn on/off libxsmm at runtime (#4455)
* enable turn on/off libxsmm at runtime by adding a global config and related API
Co-authored-by: Ubuntu <ubuntu@ip-172-31-19-194.ap-northeast-1.compute.internal>
* [Feature] Unify the cuda stream used in core library (#4480)
* Use an internal cuda stream for CopyDataFromTo
* small fix white space
* Fix to compile
* Make stream optional in copydata for compile
* fix lint issue
* Update cub functions to use internal stream
* Lint check
* Update CopyTo/CopyFrom/CopyFromTo to use internal stream
* Address comments
* Fix backward CUDA stream
* Avoid overloading CopyFromTo()
* Minor comment update
* Overload copydatafromto in cuda device api
Co-authored-by: xiny <xiny@nvidia.com>
* [Feature] Added exclude_self and output_batch to knn graph construction (Issues #4323 #4316) (#4389)
* * Added "exclude_self" and "output_batch" options to knn_graph and segmented_knn_graph
* Updated out-of-date comments on remove_edges and remove_self_loop, since they now preserve batch information
* * Changed defaults on new knn_graph and segmented_knn_graph function parameters, for compatibility; pytorch/test_geometry.py was failing
* * Added test to ensure dgl.remove_self_loop function correctly updates batch information
* * Added new knn_graph and segmented_knn_graph parameters to dgl.nn.KNNGraph and dgl.nn.SegmentedKNNGraph
* * Formatting
* * Oops, I missed the one in segmented_knn_graph when I fixed the similar thing in knn_graph
* * Fixed edge case handling when invalid k specified, since it still needs to be handled consistently for tests to pass
* Fixed context of batch info, since it must match the context of the input position data for remove_self_loop to succeed
* * Fixed batch info resulting from knn_graph when output_batch is true, for case of 3D input tensor, representing multiple segments
* * Added testing of new exclude_self and output_batch parameters on knn_graph and segmented_knn_graph, and their wrappers, KNNGraph and SegmentedKNNGraph, into the test_knn_cuda test
* * Added doc comments for new parameters
* * Added correct handling for uncommon case of k or more coincident points when excluding self edges in knn_graph and segmented_knn_graph
* Added test cases for more than k coincident points
* * Updated doc comments for output_batch parameters for clarity
* * Linter formatting fixes
* * Extracted out common function for test_knn_cpu and test_knn_cuda, to add the new test cases to test_knn_cpu
* * Rewording in doc comments
* * Removed output_batch parameter from knn_graph and segmented_knn_graph, in favour of always setting the batch information, except in knn_graph if x is a 2D tensor
Co-authored-by: Minjie Wang <wmjlyjemaine@gmail.com>
* [CI] only known devs are authorized to trigger CI (#4518)
* [CI] only known devs are authorized to trigger CI
* fix if author is null
* add comments
* [Readability] Auto fix setup.py and update-version.py (#4446)
* Auto fix update-version
* Auto fix setup.py
* Auto fix update-version
* Auto fix setup.py
* [Doc] Change random.py to random_partition.py in guide on distributed partition pipeline (#4438)
* Update distributed-preprocessing.rst
* Update
Co-authored-by: Ubuntu <ubuntu@ip-172-31-9-26.ap-northeast-1.compute.internal>
* fix unpinning when tensoradaptor is not available (#4450)
* [Doc] fix print issue in tutorial (#4459)
* [Example][Refactor] Refactor RGCN example (#4327)
* Refactor full graph entity classification
* Refactor rgcn with sampling
* README update
* Update
* Results update
* Respect default setting of self_loop=false in entity.py
* Update
* Update README
* Update for multi-gpu
* Update
* [doc] fix invalid link in user guide (#4468)
* [Example] directional_GSN for ogbg-molpcba (#4405)
* version-1
* version-2
* version-3
* update examples/README
* Update .gitignore
* update performance in README, delete scripts
* 1st approving review
* 2nd approving review
Co-authored-by: Mufei Li <mufeili1996@gmail.com>
* Clarify the message name, which is 'm'. (#4462)
Co-authored-by: Ubuntu <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
Co-authored-by: Rhett Ying <85214957+Rhett-Ying@users.noreply.github.com>
* [Refactor] Auto fix view.py. (#4461)
Co-authored-by: Ubuntu <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
Co-authored-by: Minjie Wang <wmjlyjemaine@gmail.com>
* [Example] SEAL for OGBL (#4291)
* [Example] SEAL for OGBL
* update index
* update
* fix readme typo
* add seal sampler
* modify set ops
* prefetch
* efficiency test
* update
* optimize
* fix ScatterAdd dtype issue
* update sampler style
* update
Co-authored-by: Quan Gan <coin2028@hotmail.com>
* [CI] use https instead of http (#4488)
* [BugFix] fix crash due to incorrect dtype in dgl.to_block() (#4487)
* [BugFix] fix crash due to incorrect dtype in dgl.to_block()
* fix test failure in TF
* [Feature] Make TensorAdapter Stream Aware (#4472)
* Allocate tensors in DGL's current stream
* make tensoradaptor stream-aware
* replace TAemtpy with cpu allocator
* fix typo
* try fix cpu allocation
* clean header
* redirect AllocDataSpace as well
* resolve comments
* [Build][Doc] Specify the sphinx version (#4465)
Co-authored-by: Minjie Wang <wmjlyjemaine@gmail.com>
* reformat
* reformat
* Auto fix update-version
* Auto fix setup.py
* reformat
* reformat
Co-authored-by: Ubuntu <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
Co-authored-by: Rhett Ying <85214957+Rhett-Ying@users.noreply.github.com>
Co-authored-by: Mufei Li <mufeili1996@gmail.com>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-9-26.ap-northeast-1.compute.internal>
Co-authored-by: Xin Yao <xiny@nvidia.com>
Co-authored-by: Chang Liu <chang.liu@utexas.edu>
Co-authored-by: Zhiteng Li <55398076+ZHITENGLI@users.noreply.github.com>
Co-authored-by: Minjie Wang <wmjlyjemaine@gmail.com>
Co-authored-by: rudongyu <ru_dongyu@outlook.com>
Co-authored-by: Quan Gan <coin2028@hotmail.com>
* Move mock version of dgl_sparse library to DGL main repo (#4524)
* init
* Add api doc for sparse library
* support op btwn matrices with differnt sparsity
* Fixed docstring
* addresses comments
* lint check
* change keyword format to fmt
Co-authored-by: Israt Nisa <nisisrat@amazon.com>
* [DistPart] expose timeout config for process group (#4532)
* [DistPart] expose timeout config for process group
* refine code
* Update tools/distpartitioning/data_proc_pipeline.py
Co-authored-by: Minjie Wang <wmjlyjemaine@gmail.com>
Co-authored-by: Minjie Wang <wmjlyjemaine@gmail.com>
* [Feature] Import PyTorch's CUDA stream management (#4503)
* add set_stream
* add .record_stream for NDArray and HeteroGraph
* refactor dgl stream Python APIs
* test record_stream
* add unit test for record stream
* use pytorch's stream
* fix lint
* fix cpu build
* address comments
* address comments
* add record stream tests for dgl.graph
* record frames and update dataloder
* add docstring
* update frame
* add backend check for record_stream
* remove CUDAThreadEntry::stream
* record stream for newly created formats
* fix bug
* fix cpp test
* fix None c_void_p to c_handle
* [examples]educe memory consumption (#4558)
* [examples]educe memory consumption
* reffine help message
* refine
* [Feature][REVIEW] Enable DGL cugaph nightly CI (#4525)
* Added cugraph nightly scripts
* Removed nvcr.io//nvidia/pytorch:22.04-py3 reference
Co-authored-by: Rhett Ying <85214957+Rhett-Ying@users.noreply.github.com>
* Revert "[Feature][REVIEW] Enable DGL cugaph nightly CI (#4525)" (#4563)
This reverts commit ec171c648a.
* [Misc] Add flake8 lint workflow. (#4566)
* Add pyproject.toml for autopep8.
* Add pyproject.toml for autopep8.
* Add flake8 annotation in workflow.
* remove
* add
* clean up
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* [Misc] Try use official pylint workflow. (#4568)
* polish update_version
* update pylint workflow.
* add
* revert.
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* [CI] refine stage logic (#4565)
* [CI] refine stage logic
* refine
* refine
* remove (#4570)
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* Add Pylint workflow for flake8. (#4571)
* remove
* Add pylint.
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* [Misc] Update the python version in Pylint workflow for flake8. (#4572)
* remove
* Add pylint.
* Change the python version for pylint.
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* Update pylint. (#4574)
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* [Misc] Use another workflow. (#4575)
* Update pylint.
* Use another workflow.
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* Update pylint. (#4576)
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* Update pylint.yml
* Update pylint.yml
* Delete pylint.yml
* [Misc]Add pyproject.toml for autopep8 & black. (#4543)
* Add pyproject.toml for autopep8.
* Add pyproject.toml for autopep8.
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* [Feature] Bump DLPack to v0.7 and decouple DLPack from the core library (#4454)
* rename `DLContext` to `DGLContext`
* rename `kDLGPU` to `kDLCUDA`
* replace DLTensor with DGLArray
* fix linting
* Unify DGLType and DLDataType to DGLDataType
* Fix FFI
* rename DLDeviceType to DGLDeviceType
* decouple dlpack from the core library
* fix bug
* fix lint
* fix merge
* fix build
* address comments
* rename dl_converter to dlpack_convert
* remove redundant comments
Co-authored-by: Chang Liu <chang.liu@utexas.edu>
Co-authored-by: nv-dlasalle <63612878+nv-dlasalle@users.noreply.github.com>
Co-authored-by: Xin Yao <xiny@nvidia.com>
Co-authored-by: Xin Yao <yaox12@outlook.com>
Co-authored-by: Israt Nisa <neesha295@gmail.com>
Co-authored-by: Israt Nisa <nisisrat@amazon.com>
Co-authored-by: peizhou001 <110809584+peizhou001@users.noreply.github.com>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-19-194.ap-northeast-1.compute.internal>
Co-authored-by: ndickson-nvidia <99772994+ndickson-nvidia@users.noreply.github.com>
Co-authored-by: Minjie Wang <wmjlyjemaine@gmail.com>
Co-authored-by: Rhett Ying <85214957+Rhett-Ying@users.noreply.github.com>
Co-authored-by: Hongzhi (Steve), Chen <chenhongzhi.nkcs@gmail.com>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-9-26.ap-northeast-1.compute.internal>
Co-authored-by: Zhiteng Li <55398076+ZHITENGLI@users.noreply.github.com>
Co-authored-by: rudongyu <ru_dongyu@outlook.com>
Co-authored-by: Quan Gan <coin2028@hotmail.com>
Co-authored-by: Vibhu Jawa <vibhujawa@gmail.com>
* [Deprecation] Dataset Attributes (#4546)
* Update
* CI
* CI
* Update
Co-authored-by: Ubuntu <ubuntu@ip-172-31-9-26.ap-northeast-1.compute.internal>
* [Example] Bug Fix (#4665)
* Update
* CI
* CI
* Update
* Update
Co-authored-by: Ubuntu <ubuntu@ip-172-31-9-26.ap-northeast-1.compute.internal>
* Update
* Update (#4724)
Co-authored-by: Ubuntu <ubuntu@ip-172-31-9-26.ap-northeast-1.compute.internal>
* [API Deprecation]Rename DGLHeterpGraph to DGLGraph in Py files (#4835)
* rename DGLHeterpGraph to DGLGraph
* [Sparse] Add sparse matrix C++ implementation (#4773)
* [Sparse] Add sparse matrix C++ implementation
* Add documentation
* Update
* Minor fix
* Move Python code to dgl/mock_sparse2
* Move headers to include
* lint
* Update
* Add dgl_sparse directory
* Move src code to dgl_sparse
* Add __init__.py in tests to avoid naming conflict
* Add dgl sparse so in Jenkinsfile
* Complete docstring & SparseMatrix basic op
* lint
* Disable win tests
* fix lint issue
* [Misc] clang-format auto fix. (#4831)
* [Misc] clang-format auto fix.
* blabla
* nolint
* blabla
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* [Dist] enable access DistGraph.edges via canonical etype (#4814)
* [Dist] enable access DistGraph.edges via canonical etype
* refine code
* refine test
* refine code
* Reading files in chunks to reduce the memory footprint of pyarrow (#4795)
All tasks completed.
* [Dist] Create <graph_name>_stats.txt file if it does not exist before ParMETIS execution (#4791)
* check if stats file exists, if not create one before parmetis run
* correct the typo error and correctly use constants.GRAPH_NAME
* alltoall returns tensor list with None values, which is failing torch.cat(). (#4788)
* replace batch_hetero
* [Misc] Add // NOLINT for the very long code. (#4834)
* alternative
* fix
* remove_todo
* blabl
* ablabl
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* fix (#4841)
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* a better way to init threadlocal prng (#4808)
Co-authored-by: Quan (Andy) Gan <coin2028@hotmail.com>
* [DIST] Message size to retrieve SHUFFLE_GLOBAL_NIDs is resulting in very large messages and resulting in killed process (#4790)
* Send out the message to the distributed lookup service in batches.
* Update function signature for allgather_sizes function call.
* Removed the unnecessary if statement .
* Removed logging.info message, which is not needed.
* [Misc] Minor code style fix. (#4843)
* [Misc] Change the max line length for cpp to 80 in lint.
* blabla
* blabla
* blabla
* ablabla
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
* [Sparse] Lint C++ files (#4845)
* [Dist] Fix typo of metis preprocess in dist partitin pipeline
* Fix ogb/ogbn-mag/heter-RGCN example (#4839)
Co-authored-by: Mufei Li <mufeili1996@gmail.com>
* fix issue
* [Misc] Update cpplint. (#4844)
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-16-19.ap-northeast-1.compute.internal>
Co-authored-by: czkkkkkk <zekucai@gmail.com>
Co-authored-by: Hongzhi (Steve), Chen <chenhongzhi.nkcs@gmail.com>
Co-authored-by: Steve <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
Co-authored-by: Rhett Ying <85214957+Rhett-Ying@users.noreply.github.com>
Co-authored-by: kylasa <kylasa@gmail.com>
Co-authored-by: Muhammed Fatih BALIN <m.f.balin@gmail.com>
Co-authored-by: Quan (Andy) Gan <coin2028@hotmail.com>
Co-authored-by: xiang song(charlie.song) <classicxsong@gmail.com>
Co-authored-by: Chang Liu <chang.liu@utexas.edu>
Co-authored-by: Mufei Li <mufeili1996@gmail.com>
* [API Deprecate]Remove as_heterograph and as_immutable_graph (#4851)
* Remove batch_hetero and unbatch_hetero
* [API Deprecate]Rename DGLHeterpGraph to DGLGraph in python files (#4833)
* remove training new line
* remove node_attrs and edge_attrs in batch.py (#4890)
* [API Deprecation] Remove copy_src,copy_edge,src_mul_edge in dgl.function (#4891)
* remove emb_tensor in NodeEmbedding (#4892)
* [API Deprecation]Remove 5 APIs in DGLGraph (#4902)
* [API Deprecation]Remove APIs in old dgl section in DGLGraph (#4901)
* [API Deprecation]Remove adjacency_matrix_scipy and inplace args in candidates (#4895)
* [API Deprecation] Remove add_edge in DGLGraph (#4894)
* [API Derepcation]Remove __contains__ in DGLGraph (#4937)
* [API Deprecation]Remove edge_id() and force_multi argument in edge_ids() (#4896)
* fix issue
* remove deprecated_kwargs
* remove unused import
Co-authored-by: Mufei Li <mufeili1996@gmail.com>
Co-authored-by: Chang Liu <chang.liu@utexas.edu>
Co-authored-by: nv-dlasalle <63612878+nv-dlasalle@users.noreply.github.com>
Co-authored-by: Xin Yao <xiny@nvidia.com>
Co-authored-by: Xin Yao <yaox12@outlook.com>
Co-authored-by: Israt Nisa <neesha295@gmail.com>
Co-authored-by: Israt Nisa <nisisrat@amazon.com>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-19-194.ap-northeast-1.compute.internal>
Co-authored-by: ndickson-nvidia <99772994+ndickson-nvidia@users.noreply.github.com>
Co-authored-by: Minjie Wang <wmjlyjemaine@gmail.com>
Co-authored-by: Rhett Ying <85214957+Rhett-Ying@users.noreply.github.com>
Co-authored-by: Hongzhi (Steve), Chen <chenhongzhi.nkcs@gmail.com>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-34-29.ap-northeast-1.compute.internal>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-9-26.ap-northeast-1.compute.internal>
Co-authored-by: Zhiteng Li <55398076+ZHITENGLI@users.noreply.github.com>
Co-authored-by: rudongyu <ru_dongyu@outlook.com>
Co-authored-by: Quan Gan <coin2028@hotmail.com>
Co-authored-by: Vibhu Jawa <vibhujawa@gmail.com>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-16-19.ap-northeast-1.compute.internal>
Co-authored-by: czkkkkkk <zekucai@gmail.com>
Co-authored-by: kylasa <kylasa@gmail.com>
Co-authored-by: Muhammed Fatih BALIN <m.f.balin@gmail.com>
Co-authored-by: xiang song(charlie.song) <classicxsong@gmail.com>
1236 行
44 KiB
Python
1236 行
44 KiB
Python
"""Functions for extracting subgraphs.
|
|
|
|
The module only contains functions for extracting subgraphs deterministically.
|
|
For stochastic subgraph extraction, please see functions under :mod:`dgl.sampling`.
|
|
"""
|
|
from collections.abc import Mapping
|
|
|
|
from . import backend as F
|
|
from . import graph_index, heterograph_index, utils
|
|
from ._ffi.function import _init_api
|
|
from .base import DGLError
|
|
from .heterograph import DGLGraph
|
|
from .utils import context_of, recursive_apply
|
|
|
|
__all__ = [
|
|
"node_subgraph",
|
|
"edge_subgraph",
|
|
"node_type_subgraph",
|
|
"edge_type_subgraph",
|
|
"in_subgraph",
|
|
"out_subgraph",
|
|
"khop_in_subgraph",
|
|
"khop_out_subgraph",
|
|
]
|
|
|
|
|
|
def node_subgraph(
|
|
graph, nodes, *, relabel_nodes=True, store_ids=True, output_device=None
|
|
):
|
|
"""Return a subgraph induced on the given nodes.
|
|
|
|
A node-induced subgraph is a graph with edges whose endpoints are both in the
|
|
specified node set. In addition to extracting the subgraph, DGL also copies
|
|
the features of the extracted nodes and edges to the resulting graph. The copy
|
|
is *lazy* and incurs data movement only when needed.
|
|
|
|
If the graph is heterogeneous, DGL extracts a subgraph per relation and composes
|
|
them as the resulting graph. Thus, the resulting graph has the same set of relations
|
|
as the input one.
|
|
|
|
Parameters
|
|
----------
|
|
graph : DGLGraph
|
|
The graph to extract subgraphs from.
|
|
nodes : nodes or dict[str, nodes]
|
|
The nodes to form the subgraph, which cannot have any duplicate value. The result
|
|
will be undefined otherwise. The allowed nodes formats are:
|
|
|
|
* Int Tensor: Each element is a node ID. The tensor must have the same device type
|
|
and ID data type as the graph's.
|
|
* iterable[int]: Each element is a node ID.
|
|
* Bool Tensor: Each :math:`i^{th}` element is a bool flag indicating whether
|
|
node :math:`i` is in the subgraph.
|
|
|
|
If the graph is homogeneous, one can directly pass the above formats.
|
|
Otherwise, the argument must be a dictionary with keys being node types
|
|
and values being the node IDs in the above formats.
|
|
relabel_nodes : bool, optional
|
|
If True, the extracted subgraph will only have the nodes in the specified node set
|
|
and it will relabel the nodes in order.
|
|
store_ids : bool, optional
|
|
If True, it will store the raw IDs of the extracted edges in the ``edata`` of the
|
|
resulting graph under name ``dgl.EID``; if ``relabel_nodes`` is ``True``, it will
|
|
also store the raw IDs of the specified nodes in the ``ndata`` of the resulting
|
|
graph under name ``dgl.NID``.
|
|
output_device : Framework-specific device context object, optional
|
|
The output device. Default is the same as the input graph.
|
|
|
|
Returns
|
|
-------
|
|
G : DGLGraph
|
|
The subgraph.
|
|
|
|
Notes
|
|
-----
|
|
|
|
This function discards the batch information. Please use
|
|
:func:`dgl.DGLGraph.set_batch_num_nodes`
|
|
and :func:`dgl.DGLGraph.set_batch_num_edges` on the transformed graph
|
|
to maintain the information.
|
|
|
|
Examples
|
|
--------
|
|
The following example uses PyTorch backend.
|
|
|
|
>>> import dgl
|
|
>>> import torch
|
|
|
|
Extract a subgraph from a homogeneous graph.
|
|
|
|
>>> g = dgl.graph(([0, 1, 2, 3, 4], [1, 2, 3, 4, 0])) # 5-node cycle
|
|
>>> sg = dgl.node_subgraph(g, [0, 1, 4])
|
|
>>> sg
|
|
Graph(num_nodes=3, num_edges=2,
|
|
ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([0, 2]), tensor([1, 0]))
|
|
>>> sg.ndata[dgl.NID] # original node IDs
|
|
tensor([0, 1, 4])
|
|
>>> sg.edata[dgl.EID] # original edge IDs
|
|
tensor([0, 4])
|
|
|
|
Specify nodes using a boolean mask.
|
|
|
|
>>> nodes = torch.tensor([True, True, False, False, True]) # choose nodes [0, 1, 4]
|
|
>>> dgl.node_subgraph(g, nodes)
|
|
Graph(num_nodes=3, num_edges=2,
|
|
ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
|
|
The resulting subgraph also copies features from the parent graph.
|
|
|
|
>>> g.ndata['x'] = torch.arange(10).view(5, 2)
|
|
>>> sg = dgl.node_subgraph(g, [0, 1, 4])
|
|
>>> sg
|
|
Graph(num_nodes=3, num_edges=2,
|
|
ndata_schemes={'x': Scheme(shape=(2,), dtype=torch.int64),
|
|
'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.ndata['x']
|
|
tensor([[0, 1],
|
|
[2, 3],
|
|
[8, 9]])
|
|
|
|
Extract a subgraph from a hetergeneous graph.
|
|
|
|
>>> g = dgl.heterograph({
|
|
>>> ('user', 'plays', 'game'): ([0, 1, 1, 2], [0, 0, 2, 1]),
|
|
>>> ('user', 'follows', 'user'): ([0, 1, 1], [1, 2, 2])
|
|
>>> })
|
|
>>> sub_g = dgl.node_subgraph(g, {'user': [1, 2]})
|
|
>>> sub_g
|
|
Graph(num_nodes={'game': 0, 'user': 2},
|
|
num_edges={('user', 'follows', 'user'): 2, ('user', 'plays', 'game'): 0},
|
|
metagraph=[('user', 'user', 'follows'), ('user', 'game', 'plays')])
|
|
|
|
See Also
|
|
--------
|
|
edge_subgraph
|
|
"""
|
|
if graph.is_block:
|
|
raise DGLError("Extracting subgraph from a block graph is not allowed.")
|
|
if not isinstance(nodes, Mapping):
|
|
assert (
|
|
len(graph.ntypes) == 1
|
|
), "need a dict of node type and IDs for graph with multiple node types"
|
|
nodes = {graph.ntypes[0]: nodes}
|
|
|
|
def _process_nodes(ntype, v):
|
|
if F.is_tensor(v) and F.dtype(v) == F.bool:
|
|
return F.astype(
|
|
F.nonzero_1d(F.copy_to(v, graph.device)), graph.idtype
|
|
)
|
|
else:
|
|
return utils.prepare_tensor(graph, v, 'nodes["{}"]'.format(ntype))
|
|
|
|
nodes = {ntype: _process_nodes(ntype, v) for ntype, v in nodes.items()}
|
|
device = context_of(nodes)
|
|
|
|
induced_nodes = [
|
|
nodes.get(ntype, F.copy_to(F.tensor([], graph.idtype), device))
|
|
for ntype in graph.ntypes
|
|
]
|
|
sgi = graph._graph.node_subgraph(induced_nodes, relabel_nodes)
|
|
induced_edges = sgi.induced_edges
|
|
# (BarclayII) should not write induced_nodes = sgi.induced_nodes due to the same
|
|
# bug in #1453.
|
|
induced_nodes_or_device = induced_nodes if relabel_nodes else device
|
|
subg = _create_hetero_subgraph(
|
|
graph, sgi, induced_nodes_or_device, induced_edges, store_ids=store_ids
|
|
)
|
|
return subg if output_device is None else subg.to(output_device)
|
|
|
|
|
|
DGLGraph.subgraph = utils.alias_func(node_subgraph)
|
|
|
|
|
|
def edge_subgraph(
|
|
graph,
|
|
edges,
|
|
*,
|
|
relabel_nodes=True,
|
|
store_ids=True,
|
|
output_device=None
|
|
):
|
|
"""Return a subgraph induced on the given edges.
|
|
|
|
An edge-induced subgraph is equivalent to creating a new graph using the given
|
|
edges. In addition to extracting the subgraph, DGL also copies the features
|
|
of the extracted nodes and edges to the resulting graph. The copy is *lazy*
|
|
and incurs data movement only when needed.
|
|
|
|
If the graph is heterogeneous, DGL extracts a subgraph per relation and composes
|
|
them as the resulting graph. Thus, the resulting graph has the same set of relations
|
|
as the input one.
|
|
|
|
Parameters
|
|
----------
|
|
graph : DGLGraph
|
|
The graph to extract the subgraph from.
|
|
edges : edges or dict[(str, str, str), edges]
|
|
The edges to form the subgraph. The allowed edges formats are:
|
|
|
|
* Int Tensor: Each element is an edge ID. The tensor must have the same device type
|
|
and ID data type as the graph's.
|
|
* iterable[int]: Each element is an edge ID.
|
|
* Bool Tensor: Each :math:`i^{th}` element is a bool flag indicating whether
|
|
edge :math:`i` is in the subgraph.
|
|
|
|
If the graph is homogeneous, one can directly pass the above formats.
|
|
Otherwise, the argument must be a dictionary with keys being edge types
|
|
and values being the edge IDs in the above formats.
|
|
relabel_nodes : bool, optional
|
|
If True, it will remove the isolated nodes and relabel the incident nodes in the
|
|
extracted subgraph.
|
|
store_ids : bool, optional
|
|
If True, it will store the raw IDs of the extracted edges in the ``edata`` of the
|
|
resulting graph under name ``dgl.EID``; if ``relabel_nodes`` is ``True``, it will
|
|
also store the raw IDs of the incident nodes in the ``ndata`` of the resulting
|
|
graph under name ``dgl.NID``.
|
|
output_device : Framework-specific device context object, optional
|
|
The output device. Default is the same as the input graph.
|
|
|
|
Returns
|
|
-------
|
|
G : DGLGraph
|
|
The subgraph.
|
|
|
|
Notes
|
|
-----
|
|
|
|
This function discards the batch information. Please use
|
|
:func:`dgl.DGLGraph.set_batch_num_nodes`
|
|
and :func:`dgl.DGLGraph.set_batch_num_edges` on the transformed graph
|
|
to maintain the information.
|
|
|
|
Examples
|
|
--------
|
|
The following example uses PyTorch backend.
|
|
|
|
>>> import dgl
|
|
>>> import torch
|
|
|
|
Extract a subgraph from a homogeneous graph.
|
|
|
|
>>> g = dgl.graph(([0, 1, 2, 3, 4], [1, 2, 3, 4, 0])) # 5-node cycle
|
|
>>> sg = dgl.edge_subgraph(g, [0, 4])
|
|
>>> sg
|
|
Graph(num_nodes=3, num_edges=2,
|
|
ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([0, 1]), tensor([2, 0]))
|
|
>>> sg.ndata[dgl.NID] # original node IDs
|
|
tensor([0, 4, 1])
|
|
>>> sg.edata[dgl.EID] # original edge IDs
|
|
tensor([0, 4])
|
|
|
|
Extract a subgraph without node relabeling.
|
|
|
|
>>> sg = dgl.edge_subgraph(g, [0, 4], relabel_nodes=False)
|
|
>>> sg
|
|
Graph(num_nodes=5, num_edges=2,
|
|
ndata_schemes={}
|
|
edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([0, 4]), tensor([1, 0]))
|
|
|
|
Specify edges using a boolean mask.
|
|
|
|
>>> nodes = torch.tensor([True, False, False, False, True]) # choose edges [0, 4]
|
|
>>> dgl.edge_subgraph(g, nodes)
|
|
Graph(num_nodes=3, num_edges=2,
|
|
ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
|
|
The resulting subgraph also copies features from the parent graph.
|
|
|
|
>>> g.ndata['x'] = torch.arange(10).view(5, 2)
|
|
>>> sg = dgl.edge_subgraph(g, [0, 4])
|
|
>>> sg
|
|
Graph(num_nodes=3, num_edges=2,
|
|
ndata_schemes={'x': Scheme(shape=(2,), dtype=torch.int64),
|
|
'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.ndata[dgl.NID]
|
|
tensor([0, 4, 1])
|
|
>>> sg.ndata['x']
|
|
tensor([[0, 1],
|
|
[8, 9],
|
|
[2, 3]])
|
|
|
|
Extract a subgraph from a hetergeneous graph.
|
|
|
|
>>> g = dgl.heterograph({
|
|
>>> ('user', 'plays', 'game'): ([0, 1, 1, 2], [0, 0, 2, 1]),
|
|
>>> ('user', 'follows', 'user'): ([0, 1, 1], [1, 2, 2])
|
|
>>> })
|
|
>>> sub_g = dgl.edge_subgraph(g, {('user', 'follows', 'user'): [1, 2],
|
|
... ('user', 'plays', 'game'): [2]})
|
|
>>> print(sub_g)
|
|
Graph(num_nodes={'game': 1, user': 2},
|
|
num_edges={('user', 'follows', 'user'): 2, ('user', 'plays', 'game'): 1},
|
|
metagraph=[('user', 'user', 'follows'), ('user', 'game', 'plays')])
|
|
|
|
See Also
|
|
--------
|
|
node_subgraph
|
|
"""
|
|
if graph.is_block and relabel_nodes:
|
|
raise DGLError("Extracting subgraph from a block graph is not allowed.")
|
|
if not isinstance(edges, Mapping):
|
|
assert (
|
|
len(graph.canonical_etypes) == 1
|
|
), "need a dict of edge type and IDs for graph with multiple edge types"
|
|
edges = {graph.canonical_etypes[0]: edges}
|
|
|
|
def _process_edges(etype, e):
|
|
if F.is_tensor(e) and F.dtype(e) == F.bool:
|
|
return F.astype(
|
|
F.nonzero_1d(F.copy_to(e, graph.device)), graph.idtype
|
|
)
|
|
else:
|
|
return utils.prepare_tensor(graph, e, 'edges["{}"]'.format(etype))
|
|
|
|
edges = {graph.to_canonical_etype(etype): e for etype, e in edges.items()}
|
|
edges = {etype: _process_edges(etype, e) for etype, e in edges.items()}
|
|
device = context_of(edges)
|
|
induced_edges = [
|
|
edges.get(cetype, F.copy_to(F.tensor([], graph.idtype), device))
|
|
for cetype in graph.canonical_etypes
|
|
]
|
|
|
|
sgi = graph._graph.edge_subgraph(induced_edges, not relabel_nodes)
|
|
induced_nodes_or_device = sgi.induced_nodes if relabel_nodes else device
|
|
subg = _create_hetero_subgraph(
|
|
graph, sgi, induced_nodes_or_device, induced_edges, store_ids=store_ids
|
|
)
|
|
return subg if output_device is None else subg.to(output_device)
|
|
|
|
|
|
DGLGraph.edge_subgraph = utils.alias_func(edge_subgraph)
|
|
|
|
|
|
def in_subgraph(
|
|
graph, nodes, *, relabel_nodes=False, store_ids=True, output_device=None
|
|
):
|
|
"""Return the subgraph induced on the inbound edges of all the edge types of the
|
|
given nodes.
|
|
|
|
An in subgraph is equivalent to creating a new graph using the incoming edges of the
|
|
given nodes. In addition to extracting the subgraph, DGL also copies the features of
|
|
the extracted nodes and edges to the resulting graph. The copy is *lazy* and incurs
|
|
data movement only when needed.
|
|
|
|
If the graph is heterogeneous, DGL extracts a subgraph per relation and composes
|
|
them as the resulting graph. Thus, the resulting graph has the same set of relations
|
|
as the input one.
|
|
|
|
Parameters
|
|
----------
|
|
graph : DGLGraph
|
|
The input graph.
|
|
nodes : nodes or dict[str, nodes]
|
|
The nodes to form the subgraph, which cannot have any duplicate value. The result
|
|
will be undefined otherwise. The allowed nodes formats are:
|
|
|
|
* Int Tensor: Each element is a node ID. The tensor must have the same device type
|
|
and ID data type as the graph's.
|
|
* iterable[int]: Each element is a node ID.
|
|
|
|
If the graph is homogeneous, one can directly pass the above formats.
|
|
Otherwise, the argument must be a dictionary with keys being node types
|
|
and values being the node IDs in the above formats.
|
|
relabel_nodes : bool, optional
|
|
If True, it will remove the isolated nodes and relabel the rest nodes in the
|
|
extracted subgraph.
|
|
store_ids : bool, optional
|
|
If True, it will store the raw IDs of the extracted edges in the ``edata`` of the
|
|
resulting graph under name ``dgl.EID``; if ``relabel_nodes`` is ``True``, it will
|
|
also store the raw IDs of the extracted nodes in the ``ndata`` of the resulting
|
|
graph under name ``dgl.NID``.
|
|
output_device : Framework-specific device context object, optional
|
|
The output device. Default is the same as the input graph.
|
|
|
|
Returns
|
|
-------
|
|
DGLGraph
|
|
The subgraph.
|
|
|
|
Notes
|
|
-----
|
|
|
|
This function discards the batch information. Please use
|
|
:func:`dgl.DGLGraph.set_batch_num_nodes`
|
|
and :func:`dgl.DGLGraph.set_batch_num_edges` on the transformed graph
|
|
to maintain the information.
|
|
|
|
Examples
|
|
--------
|
|
The following example uses PyTorch backend.
|
|
|
|
>>> import dgl
|
|
>>> import torch
|
|
|
|
Extract a subgraph from a homogeneous graph.
|
|
|
|
>>> g = dgl.graph(([0, 1, 2, 3, 4], [1, 2, 3, 4, 0])) # 5-node cycle
|
|
>>> g.edata['w'] = torch.arange(10).view(5, 2)
|
|
>>> sg = dgl.in_subgraph(g, [2, 0])
|
|
>>> sg
|
|
Graph(num_nodes=5, num_edges=2,
|
|
ndata_schemes={}
|
|
edata_schemes={'w': Scheme(shape=(2,), dtype=torch.int64),
|
|
'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([1, 4]), tensor([2, 0]))
|
|
>>> sg.edata[dgl.EID] # original edge IDs
|
|
tensor([1, 4])
|
|
>>> sg.edata['w'] # also extract the features
|
|
tensor([[2, 3],
|
|
[8, 9]])
|
|
|
|
Extract a subgraph with node labeling.
|
|
|
|
>>> sg = dgl.in_subgraph(g, [2, 0], relabel_nodes=True)
|
|
>>> sg
|
|
Graph(num_nodes=4, num_edges=2,
|
|
ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64}
|
|
edata_schemes={'w': Scheme(shape=(2,), dtype=torch.int64),
|
|
'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([1, 3]), tensor([2, 0]))
|
|
>>> sg.edata[dgl.EID] # original edge IDs
|
|
tensor([1, 4])
|
|
>>> sg.ndata[dgl.NID] # original node IDs
|
|
tensor([0, 1, 2, 4])
|
|
|
|
Extract a subgraph from a heterogeneous graph.
|
|
|
|
>>> g = dgl.heterograph({
|
|
... ('user', 'plays', 'game'): ([0, 1, 1, 2], [0, 0, 2, 1]),
|
|
... ('user', 'follows', 'user'): ([0, 1, 1], [1, 2, 2])})
|
|
>>> sub_g = g.in_subgraph({'user': [2], 'game': [2]})
|
|
>>> sub_g
|
|
Graph(num_nodes={'game': 3, 'user': 3},
|
|
num_edges={('user', 'plays', 'game'): 1, ('user', 'follows', 'user'): 2},
|
|
metagraph=[('user', 'game', 'plays'), ('user', 'user', 'follows')])
|
|
|
|
See also
|
|
--------
|
|
out_subgraph
|
|
"""
|
|
if graph.is_block:
|
|
raise DGLError("Extracting subgraph of a block graph is not allowed.")
|
|
if not isinstance(nodes, dict):
|
|
if len(graph.ntypes) > 1:
|
|
raise DGLError(
|
|
"Must specify node type when the graph is not homogeneous."
|
|
)
|
|
nodes = {graph.ntypes[0]: nodes}
|
|
nodes = utils.prepare_tensor_dict(graph, nodes, "nodes")
|
|
device = context_of(nodes)
|
|
nodes_all_types = [
|
|
F.to_dgl_nd(
|
|
nodes.get(ntype, F.copy_to(F.tensor([], graph.idtype), device))
|
|
)
|
|
for ntype in graph.ntypes
|
|
]
|
|
|
|
sgi = _CAPI_DGLInSubgraph(graph._graph, nodes_all_types, relabel_nodes)
|
|
induced_nodes_or_device = sgi.induced_nodes if relabel_nodes else device
|
|
induced_edges = sgi.induced_edges
|
|
subg = _create_hetero_subgraph(
|
|
graph, sgi, induced_nodes_or_device, induced_edges, store_ids=store_ids
|
|
)
|
|
return subg if output_device is None else subg.to(output_device)
|
|
|
|
|
|
DGLGraph.in_subgraph = utils.alias_func(in_subgraph)
|
|
|
|
|
|
def out_subgraph(
|
|
graph, nodes, *, relabel_nodes=False, store_ids=True, output_device=None
|
|
):
|
|
"""Return the subgraph induced on the outbound edges of all the edge types of the
|
|
given nodes.
|
|
|
|
An out subgraph is equivalent to creating a new graph using the outcoming edges of
|
|
the given nodes. In addition to extracting the subgraph, DGL also copies the features
|
|
of the extracted nodes and edges to the resulting graph. The copy is *lazy* and incurs
|
|
data movement only when needed.
|
|
|
|
If the graph is heterogeneous, DGL extracts a subgraph per relation and composes
|
|
them as the resulting graph. Thus, the resulting graph has the same set of relations
|
|
as the input one.
|
|
|
|
Parameters
|
|
----------
|
|
graph : DGLGraph
|
|
The input graph.
|
|
nodes : nodes or dict[str, nodes]
|
|
The nodes to form the subgraph, which cannot have any duplicate value. The result
|
|
will be undefined otherwise. The allowed nodes formats are:
|
|
|
|
* Int Tensor: Each element is a node ID. The tensor must have the same device type
|
|
and ID data type as the graph's.
|
|
* iterable[int]: Each element is a node ID.
|
|
|
|
If the graph is homogeneous, one can directly pass the above formats.
|
|
Otherwise, the argument must be a dictionary with keys being node types
|
|
and values being the node IDs in the above formats.
|
|
relabel_nodes : bool, optional
|
|
If True, it will remove the isolated nodes and relabel the rest nodes in the
|
|
extracted subgraph.
|
|
store_ids : bool, optional
|
|
If True, it will store the raw IDs of the extracted edges in the ``edata`` of the
|
|
resulting graph under name ``dgl.EID``; if ``relabel_nodes`` is ``True``, it will
|
|
also store the raw IDs of the extracted nodes in the ``ndata`` of the resulting
|
|
graph under name ``dgl.NID``.
|
|
output_device : Framework-specific device context object, optional
|
|
The output device. Default is the same as the input graph.
|
|
|
|
Returns
|
|
-------
|
|
DGLGraph
|
|
The subgraph.
|
|
|
|
Notes
|
|
-----
|
|
|
|
This function discards the batch information. Please use
|
|
:func:`dgl.DGLGraph.set_batch_num_nodes`
|
|
and :func:`dgl.DGLGraph.set_batch_num_edges` on the transformed graph
|
|
to maintain the information.
|
|
|
|
Examples
|
|
--------
|
|
The following example uses PyTorch backend.
|
|
|
|
>>> import dgl
|
|
>>> import torch
|
|
|
|
Extract a subgraph from a homogeneous graph.
|
|
|
|
>>> g = dgl.graph(([0, 1, 2, 3, 4], [1, 2, 3, 4, 0])) # 5-node cycle
|
|
>>> g.edata['w'] = torch.arange(10).view(5, 2)
|
|
>>> sg = dgl.out_subgraph(g, [2, 0])
|
|
>>> sg
|
|
Graph(num_nodes=5, num_edges=2,
|
|
ndata_schemes={}
|
|
edata_schemes={'w': Scheme(shape=(2,), dtype=torch.int64),
|
|
'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([2, 0]), tensor([3, 1]))
|
|
>>> sg.edata[dgl.EID] # original edge IDs
|
|
tensor([2, 0])
|
|
>>> sg.edata['w'] # also extract the features
|
|
tensor([[4, 5],
|
|
[0, 1]])
|
|
|
|
Extract a subgraph with node labeling.
|
|
|
|
>>> sg = dgl.out_subgraph(g, [2, 0], relabel_nodes=True)
|
|
>>> sg
|
|
Graph(num_nodes=4, num_edges=2,
|
|
ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'w': Scheme(shape=(2,), dtype=torch.int64),
|
|
'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([2, 0]), tensor([3, 1]))
|
|
>>> sg.edata[dgl.EID] # original edge IDs
|
|
tensor([2, 0])
|
|
>>> sg.ndata[dgl.NID] # original node IDs
|
|
tensor([0, 1, 2, 3])
|
|
|
|
Extract a subgraph from a heterogeneous graph.
|
|
|
|
>>> g = dgl.heterograph({
|
|
... ('user', 'plays', 'game'): ([0, 1, 1, 2], [0, 0, 2, 1]),
|
|
... ('user', 'follows', 'user'): ([0, 1, 1], [1, 2, 2])})
|
|
>>> sub_g = g.out_subgraph({'user': [1]})
|
|
>>> sub_g
|
|
Graph(num_nodes={'game': 3, 'user': 3},
|
|
num_edges={('user', 'plays', 'game'): 2, ('user', 'follows', 'user'): 2},
|
|
metagraph=[('user', 'game', 'plays'), ('user', 'user', 'follows')])
|
|
|
|
See also
|
|
--------
|
|
in_subgraph
|
|
"""
|
|
if graph.is_block:
|
|
raise DGLError("Extracting subgraph of a block graph is not allowed.")
|
|
if not isinstance(nodes, dict):
|
|
if len(graph.ntypes) > 1:
|
|
raise DGLError(
|
|
"Must specify node type when the graph is not homogeneous."
|
|
)
|
|
nodes = {graph.ntypes[0]: nodes}
|
|
nodes = utils.prepare_tensor_dict(graph, nodes, "nodes")
|
|
device = context_of(nodes)
|
|
nodes_all_types = [
|
|
F.to_dgl_nd(
|
|
nodes.get(ntype, F.copy_to(F.tensor([], graph.idtype), device))
|
|
)
|
|
for ntype in graph.ntypes
|
|
]
|
|
|
|
sgi = _CAPI_DGLOutSubgraph(graph._graph, nodes_all_types, relabel_nodes)
|
|
induced_nodes_or_device = sgi.induced_nodes if relabel_nodes else device
|
|
induced_edges = sgi.induced_edges
|
|
subg = _create_hetero_subgraph(
|
|
graph, sgi, induced_nodes_or_device, induced_edges, store_ids=store_ids
|
|
)
|
|
return subg if output_device is None else subg.to(output_device)
|
|
|
|
|
|
DGLGraph.out_subgraph = utils.alias_func(out_subgraph)
|
|
|
|
|
|
def khop_in_subgraph(
|
|
graph, nodes, k, *, relabel_nodes=True, store_ids=True, output_device=None
|
|
):
|
|
"""Return the subgraph induced by k-hop in-neighborhood of the specified node(s).
|
|
|
|
We can expand a set of nodes by including the predecessors of them. From a
|
|
specified node set, a k-hop in subgraph is obtained by first repeating the node set
|
|
expansion for k times and then creating a node induced subgraph. In addition to
|
|
extracting the subgraph, DGL also copies the features of the extracted nodes and
|
|
edges to the resulting graph. The copy is *lazy* and incurs data movement only
|
|
when needed.
|
|
|
|
If the graph is heterogeneous, DGL extracts a subgraph per relation and composes
|
|
them as the resulting graph. Thus the resulting graph has the same set of relations
|
|
as the input one.
|
|
|
|
Parameters
|
|
----------
|
|
graph : DGLGraph
|
|
The input graph.
|
|
nodes : nodes or dict[str, nodes]
|
|
The starting node(s) to expand, which cannot have any duplicate value. The result
|
|
will be undefined otherwise. The allowed formats are:
|
|
|
|
* Int: ID of a single node.
|
|
* Int Tensor: Each element is a node ID. The tensor must have the same device
|
|
type and ID data type as the graph's.
|
|
* iterable[int]: Each element is a node ID.
|
|
|
|
If the graph is homogeneous, one can directly pass the above formats.
|
|
Otherwise, the argument must be a dictionary with keys being node types
|
|
and values being the node IDs in the above formats.
|
|
k : int
|
|
The number of hops.
|
|
relabel_nodes : bool, optional
|
|
If True, it will remove the isolated nodes and relabel the rest nodes in the
|
|
extracted subgraph.
|
|
store_ids : bool, optional
|
|
If True, it will store the raw IDs of the extracted edges in the ``edata`` of the
|
|
resulting graph under name ``dgl.EID``; if ``relabel_nodes`` is ``True``, it will
|
|
also store the raw IDs of the extracted nodes in the ``ndata`` of the resulting
|
|
graph under name ``dgl.NID``.
|
|
output_device : Framework-specific device context object, optional
|
|
The output device. Default is the same as the input graph.
|
|
|
|
Returns
|
|
-------
|
|
DGLGraph
|
|
The subgraph.
|
|
Tensor or dict[str, Tensor], optional
|
|
The new IDs of the input :attr:`nodes` after node relabeling. This is returned
|
|
only when :attr:`relabel_nodes` is True. It is in the same form as :attr:`nodes`.
|
|
|
|
Notes
|
|
-----
|
|
|
|
When k is 1, the result subgraph is different from the one obtained by
|
|
:func:`dgl.in_subgraph`. The 1-hop in subgraph also includes the edges
|
|
among the neighborhood.
|
|
|
|
Examples
|
|
--------
|
|
The following example uses PyTorch backend.
|
|
|
|
>>> import dgl
|
|
>>> import torch
|
|
|
|
Extract a two-hop subgraph from a homogeneous graph.
|
|
|
|
>>> g = dgl.graph(([1, 1, 2, 3, 4], [0, 2, 0, 4, 2]))
|
|
>>> g.edata['w'] = torch.arange(10).view(5, 2)
|
|
>>> sg, inverse_indices = dgl.khop_in_subgraph(g, 0, k=2)
|
|
>>> sg
|
|
Graph(num_nodes=4, num_edges=4,
|
|
ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'w': Scheme(shape=(2,), dtype=torch.int64),
|
|
'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([1, 1, 2, 3]), tensor([0, 2, 0, 2]))
|
|
>>> sg.edata[dgl.EID] # original edge IDs
|
|
tensor([0, 1, 2, 4])
|
|
>>> sg.edata['w'] # also extract the features
|
|
tensor([[0, 1],
|
|
[2, 3],
|
|
[4, 5],
|
|
[8, 9]])
|
|
>>> inverse_indices
|
|
tensor([0])
|
|
|
|
Extract a subgraph from a heterogeneous graph.
|
|
|
|
>>> g = dgl.heterograph({
|
|
... ('user', 'plays', 'game'): ([0, 1, 1, 2], [0, 0, 2, 1]),
|
|
... ('user', 'follows', 'user'): ([0, 1, 1], [1, 2, 2])})
|
|
>>> sg, inverse_indices = dgl.khop_in_subgraph(g, {'game': 0}, k=2)
|
|
>>> sg
|
|
Graph(num_nodes={'game': 1, 'user': 2},
|
|
num_edges={('user', 'follows', 'user'): 1, ('user', 'plays', 'game'): 2},
|
|
metagraph=[('user', 'user', 'follows'), ('user', 'game', 'plays')])
|
|
>>> inverse_indices
|
|
{'game': tensor([0])}
|
|
|
|
See also
|
|
--------
|
|
khop_out_subgraph
|
|
"""
|
|
if graph.is_block:
|
|
raise DGLError("Extracting subgraph of a block graph is not allowed.")
|
|
|
|
is_mapping = isinstance(nodes, Mapping)
|
|
if not is_mapping:
|
|
assert (
|
|
len(graph.ntypes) == 1
|
|
), "need a dict of node type and IDs for graph with multiple node types"
|
|
nodes = {graph.ntypes[0]: nodes}
|
|
|
|
for nty, nty_nodes in nodes.items():
|
|
nodes[nty] = utils.prepare_tensor(
|
|
graph, nty_nodes, 'nodes["{}"]'.format(nty)
|
|
)
|
|
|
|
last_hop_nodes = nodes
|
|
k_hop_nodes_ = [last_hop_nodes]
|
|
device = context_of(nodes)
|
|
place_holder = F.copy_to(F.tensor([], dtype=graph.idtype), device)
|
|
for _ in range(k):
|
|
current_hop_nodes = {nty: [] for nty in graph.ntypes}
|
|
for cetype in graph.canonical_etypes:
|
|
srctype, _, dsttype = cetype
|
|
in_nbrs, _ = graph.in_edges(
|
|
last_hop_nodes.get(dsttype, place_holder), etype=cetype
|
|
)
|
|
current_hop_nodes[srctype].append(in_nbrs)
|
|
for nty in graph.ntypes:
|
|
if len(current_hop_nodes[nty]) == 0:
|
|
current_hop_nodes[nty] = place_holder
|
|
continue
|
|
current_hop_nodes[nty] = F.unique(
|
|
F.cat(current_hop_nodes[nty], dim=0)
|
|
)
|
|
k_hop_nodes_.append(current_hop_nodes)
|
|
last_hop_nodes = current_hop_nodes
|
|
|
|
k_hop_nodes = dict()
|
|
inverse_indices = dict()
|
|
for nty in graph.ntypes:
|
|
k_hop_nodes[nty], inverse_indices[nty] = F.unique(
|
|
F.cat(
|
|
[
|
|
hop_nodes.get(nty, place_holder)
|
|
for hop_nodes in k_hop_nodes_
|
|
],
|
|
dim=0,
|
|
),
|
|
return_inverse=True,
|
|
)
|
|
|
|
sub_g = node_subgraph(
|
|
graph, k_hop_nodes, relabel_nodes=relabel_nodes, store_ids=store_ids
|
|
)
|
|
if output_device is not None:
|
|
sub_g = sub_g.to(output_device)
|
|
if relabel_nodes:
|
|
if is_mapping:
|
|
seed_inverse_indices = dict()
|
|
for nty in nodes:
|
|
seed_inverse_indices[nty] = F.slice_axis(
|
|
inverse_indices[nty], axis=0, begin=0, end=len(nodes[nty])
|
|
)
|
|
else:
|
|
seed_inverse_indices = F.slice_axis(
|
|
inverse_indices[nty], axis=0, begin=0, end=len(nodes[nty])
|
|
)
|
|
if output_device is not None:
|
|
seed_inverse_indices = recursive_apply(
|
|
seed_inverse_indices, lambda x: F.copy_to(x, output_device)
|
|
)
|
|
return sub_g, seed_inverse_indices
|
|
else:
|
|
return sub_g
|
|
|
|
|
|
DGLGraph.khop_in_subgraph = utils.alias_func(khop_in_subgraph)
|
|
|
|
|
|
def khop_out_subgraph(
|
|
graph, nodes, k, *, relabel_nodes=True, store_ids=True, output_device=None
|
|
):
|
|
"""Return the subgraph induced by k-hop out-neighborhood of the specified node(s).
|
|
|
|
We can expand a set of nodes by including the successors of them. From a
|
|
specified node set, a k-hop out subgraph is obtained by first repeating the node set
|
|
expansion for k times and then creating a node induced subgraph. In addition to
|
|
extracting the subgraph, DGL also copies the features of the extracted nodes and
|
|
edges to the resulting graph. The copy is *lazy* and incurs data movement only
|
|
when needed.
|
|
|
|
If the graph is heterogeneous, DGL extracts a subgraph per relation and composes
|
|
them as the resulting graph. Thus the resulting graph has the same set of relations
|
|
as the input one.
|
|
|
|
Parameters
|
|
----------
|
|
graph : DGLGraph
|
|
The input graph.
|
|
nodes : nodes or dict[str, nodes]
|
|
The starting node(s) to expand, which cannot have any duplicate value. The result
|
|
will be undefined otherwise. The allowed formats are:
|
|
|
|
* Int: ID of a single node.
|
|
* Int Tensor: Each element is a node ID. The tensor must have the same device
|
|
type and ID data type as the graph's.
|
|
* iterable[int]: Each element is a node ID.
|
|
|
|
If the graph is homogeneous, one can directly pass the above formats.
|
|
Otherwise, the argument must be a dictionary with keys being node types
|
|
and values being the node IDs in the above formats.
|
|
k : int
|
|
The number of hops.
|
|
relabel_nodes : bool, optional
|
|
If True, it will remove the isolated nodes and relabel the rest nodes in the
|
|
extracted subgraph.
|
|
store_ids : bool, optional
|
|
If True, it will store the raw IDs of the extracted edges in the ``edata`` of the
|
|
resulting graph under name ``dgl.EID``; if ``relabel_nodes`` is ``True``, it will
|
|
also store the raw IDs of the extracted nodes in the ``ndata`` of the resulting
|
|
graph under name ``dgl.NID``.
|
|
output_device : Framework-specific device context object, optional
|
|
The output device. Default is the same as the input graph.
|
|
|
|
Returns
|
|
-------
|
|
DGLGraph
|
|
The subgraph.
|
|
Tensor or dict[str, Tensor], optional
|
|
The new IDs of the input :attr:`nodes` after node relabeling. This is returned
|
|
only when :attr:`relabel_nodes` is True. It is in the same form as :attr:`nodes`.
|
|
|
|
Notes
|
|
-----
|
|
|
|
When k is 1, the result subgraph is different from the one obtained by
|
|
:func:`dgl.out_subgraph`. The 1-hop out subgraph also includes the edges
|
|
among the neighborhood.
|
|
|
|
Examples
|
|
--------
|
|
The following example uses PyTorch backend.
|
|
|
|
>>> import dgl
|
|
>>> import torch
|
|
|
|
Extract a two-hop subgraph from a homogeneous graph.
|
|
|
|
>>> g = dgl.graph(([0, 2, 0, 4, 2], [1, 1, 2, 3, 4]))
|
|
>>> g.edata['w'] = torch.arange(10).view(5, 2)
|
|
>>> sg, inverse_indices = dgl.khop_out_subgraph(g, 0, k=2)
|
|
>>> sg
|
|
Graph(num_nodes=4, num_edges=4,
|
|
ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64)}
|
|
edata_schemes={'w': Scheme(shape=(2,), dtype=torch.int64),
|
|
'_ID': Scheme(shape=(), dtype=torch.int64)})
|
|
>>> sg.edges()
|
|
(tensor([0, 0, 2, 2]), tensor([1, 2, 1, 3]))
|
|
>>> sg.edata[dgl.EID] # original edge IDs
|
|
tensor([0, 2, 1, 4])
|
|
>>> sg.edata['w'] # also extract the features
|
|
tensor([[0, 1],
|
|
[4, 5],
|
|
[2, 3],
|
|
[8, 9]])
|
|
>>> inverse_indices
|
|
tensor([0])
|
|
|
|
Extract a subgraph from a heterogeneous graph.
|
|
|
|
>>> g = dgl.heterograph({
|
|
... ('user', 'plays', 'game'): ([0, 1, 1, 2], [0, 0, 2, 1]),
|
|
... ('user', 'follows', 'user'): ([0, 1], [1, 3])})
|
|
>>> sg, inverse_indices = dgl.khop_out_subgraph(g, {'user': 0}, k=2)
|
|
>>> sg
|
|
Graph(num_nodes={'game': 2, 'user': 3},
|
|
num_edges={('user', 'follows', 'user'): 2, ('user', 'plays', 'game'): 2},
|
|
metagraph=[('user', 'user', 'follows'), ('user', 'game', 'plays')])
|
|
>>> inverse_indices
|
|
{'user': tensor([0])}
|
|
|
|
See also
|
|
--------
|
|
khop_in_subgraph
|
|
"""
|
|
if graph.is_block:
|
|
raise DGLError("Extracting subgraph of a block graph is not allowed.")
|
|
|
|
is_mapping = isinstance(nodes, Mapping)
|
|
if not is_mapping:
|
|
assert (
|
|
len(graph.ntypes) == 1
|
|
), "need a dict of node type and IDs for graph with multiple node types"
|
|
nodes = {graph.ntypes[0]: nodes}
|
|
|
|
for nty, nty_nodes in nodes.items():
|
|
nodes[nty] = utils.prepare_tensor(
|
|
graph, nty_nodes, 'nodes["{}"]'.format(nty)
|
|
)
|
|
|
|
last_hop_nodes = nodes
|
|
k_hop_nodes_ = [last_hop_nodes]
|
|
device = context_of(nodes)
|
|
place_holder = F.copy_to(F.tensor([], dtype=graph.idtype), device)
|
|
for _ in range(k):
|
|
current_hop_nodes = {nty: [] for nty in graph.ntypes}
|
|
for cetype in graph.canonical_etypes:
|
|
srctype, _, dsttype = cetype
|
|
_, out_nbrs = graph.out_edges(
|
|
last_hop_nodes.get(srctype, place_holder), etype=cetype
|
|
)
|
|
current_hop_nodes[dsttype].append(out_nbrs)
|
|
for nty in graph.ntypes:
|
|
if len(current_hop_nodes[nty]) == 0:
|
|
current_hop_nodes[nty] = place_holder
|
|
continue
|
|
current_hop_nodes[nty] = F.unique(
|
|
F.cat(current_hop_nodes[nty], dim=0)
|
|
)
|
|
k_hop_nodes_.append(current_hop_nodes)
|
|
last_hop_nodes = current_hop_nodes
|
|
|
|
k_hop_nodes = dict()
|
|
inverse_indices = dict()
|
|
for nty in graph.ntypes:
|
|
k_hop_nodes[nty], inverse_indices[nty] = F.unique(
|
|
F.cat(
|
|
[
|
|
hop_nodes.get(nty, place_holder)
|
|
for hop_nodes in k_hop_nodes_
|
|
],
|
|
dim=0,
|
|
),
|
|
return_inverse=True,
|
|
)
|
|
|
|
sub_g = node_subgraph(
|
|
graph, k_hop_nodes, relabel_nodes=relabel_nodes, store_ids=store_ids
|
|
)
|
|
if output_device is not None:
|
|
sub_g = sub_g.to(output_device)
|
|
if relabel_nodes:
|
|
if is_mapping:
|
|
seed_inverse_indices = dict()
|
|
for nty in nodes:
|
|
seed_inverse_indices[nty] = F.slice_axis(
|
|
inverse_indices[nty], axis=0, begin=0, end=len(nodes[nty])
|
|
)
|
|
else:
|
|
seed_inverse_indices = F.slice_axis(
|
|
inverse_indices[nty], axis=0, begin=0, end=len(nodes[nty])
|
|
)
|
|
if output_device is not None:
|
|
seed_inverse_indices = recursive_apply(
|
|
seed_inverse_indices, lambda x: F.copy_to(x, output_device)
|
|
)
|
|
return sub_g, seed_inverse_indices
|
|
else:
|
|
return sub_g
|
|
|
|
|
|
DGLGraph.khop_out_subgraph = utils.alias_func(khop_out_subgraph)
|
|
|
|
|
|
def node_type_subgraph(graph, ntypes, output_device=None):
|
|
"""Return the subgraph induced on given node types.
|
|
|
|
A node-type-induced subgraph contains all the nodes of the given subset of
|
|
the node types of a graph and any edges whose endpoints are both in this subset.
|
|
In addition to extracting the subgraph, DGL also copies the features of the
|
|
extracted nodes and edges to the resulting graph.
|
|
The copy is *lazy* and incurs data movement only when needed.
|
|
|
|
Parameters
|
|
----------
|
|
graph : DGLGraph
|
|
The graph to extract subgraphs from.
|
|
ntypes : list[str]
|
|
The type names of the nodes in the subgraph.
|
|
output_device : Framework-specific device context object, optional
|
|
The output device. Default is the same as the input graph.
|
|
|
|
Returns
|
|
-------
|
|
G : DGLGraph
|
|
The subgraph.
|
|
|
|
Notes
|
|
-----
|
|
|
|
This function discards the batch information. Please use
|
|
:func:`dgl.DGLGraph.set_batch_num_nodes`
|
|
and :func:`dgl.DGLGraph.set_batch_num_edges` on the transformed graph
|
|
to maintain the information.
|
|
|
|
Examples
|
|
--------
|
|
The following example uses PyTorch backend.
|
|
|
|
>>> import dgl
|
|
>>> import torch
|
|
|
|
Instantiate a heterograph.
|
|
|
|
>>> g = dgl.heterograph({
|
|
>>> ('user', 'plays', 'game'): ([0, 1, 1, 2], [0, 0, 2, 1]),
|
|
>>> ('user', 'follows', 'user'): ([0, 1, 1], [1, 2, 2])
|
|
>>> })
|
|
>>> # Set node features
|
|
>>> g.nodes['user'].data['h'] = torch.tensor([[0.], [1.], [2.]])
|
|
|
|
Get subgraphs.
|
|
|
|
>>> sub_g = g.node_type_subgraph(['user'])
|
|
>>> print(sub_g)
|
|
Graph(num_nodes=3, num_edges=3,
|
|
ndata_schemes={'h': Scheme(shape=(1,), dtype=torch.float32)}
|
|
edata_schemes={})
|
|
|
|
Get the extracted node features.
|
|
|
|
>>> sub_g.nodes['user'].data['h']
|
|
tensor([[0.],
|
|
[1.],
|
|
[2.]])
|
|
|
|
See Also
|
|
--------
|
|
edge_type_subgraph
|
|
"""
|
|
ntid = [graph.get_ntype_id(ntype) for ntype in ntypes]
|
|
stids, dtids, etids = graph._graph.metagraph.edges("eid")
|
|
stids, dtids, etids = stids.tonumpy(), dtids.tonumpy(), etids.tonumpy()
|
|
etypes = []
|
|
for stid, dtid, etid in zip(stids, dtids, etids):
|
|
if stid in ntid and dtid in ntid:
|
|
etypes.append(graph.canonical_etypes[etid])
|
|
if len(etypes) == 0:
|
|
raise DGLError("There are no edges among nodes of the specified types.")
|
|
return edge_type_subgraph(graph, etypes, output_device=output_device)
|
|
|
|
|
|
DGLGraph.node_type_subgraph = utils.alias_func(node_type_subgraph)
|
|
|
|
|
|
def edge_type_subgraph(graph, etypes, output_device=None):
|
|
"""Return the subgraph induced on given edge types.
|
|
|
|
An edge-type-induced subgraph contains all the edges of the given subset of
|
|
the edge types of a graph. It also contains all nodes of a particular type
|
|
if some nodes of the type are incident to these edges.
|
|
In addition to extracting the subgraph, DGL also copies the features of the
|
|
extracted nodes and edges to the resulting graph.
|
|
The copy is *lazy* and incurs data movement only when needed.
|
|
|
|
Parameters
|
|
----------
|
|
graph : DGLGraph
|
|
The graph to extract subgraphs from.
|
|
etypes : list[str] or list[(str, str, str)]
|
|
The type names of the edges in the subgraph. The allowed type name
|
|
formats are:
|
|
|
|
* ``(str, str, str)`` for source node type, edge type and destination node type.
|
|
* or one ``str`` for the edge type name if the name can uniquely identify a
|
|
triplet format in the graph.
|
|
output_device : Framework-specific device context object, optional
|
|
The output device. Default is the same as the input graph.
|
|
|
|
Returns
|
|
-------
|
|
G : DGLGraph
|
|
The subgraph.
|
|
|
|
Notes
|
|
-----
|
|
|
|
This function discards the batch information. Please use
|
|
:func:`dgl.DGLGraph.set_batch_num_nodes`
|
|
and :func:`dgl.DGLGraph.set_batch_num_edges` on the transformed graph
|
|
to maintain the information.
|
|
|
|
Examples
|
|
--------
|
|
The following example uses PyTorch backend.
|
|
|
|
>>> import dgl
|
|
>>> import torch
|
|
|
|
Instantiate a heterograph.
|
|
|
|
>>> g = dgl.heterograph({
|
|
>>> ('user', 'plays', 'game'): ([0, 1, 1, 2], [0, 0, 2, 1]),
|
|
>>> ('user', 'follows', 'user'): ([0, 1, 1], [1, 2, 2])
|
|
>>> })
|
|
>>> # Set edge features
|
|
>>> g.edges['follows'].data['h'] = torch.tensor([[0.], [1.], [2.]])
|
|
|
|
Get subgraphs.
|
|
|
|
>>> sub_g = g.edge_type_subgraph(['follows'])
|
|
>>> sub_g
|
|
Graph(num_nodes=3, num_edges=3,
|
|
ndata_schemes={}
|
|
edata_schemes={'h': Scheme(shape=(1,), dtype=torch.float32)})
|
|
|
|
Get the shared edge features.
|
|
|
|
>>> sub_g.edges['follows'].data['h']
|
|
tensor([[0.],
|
|
[1.],
|
|
[2.]])
|
|
|
|
See Also
|
|
--------
|
|
node_type_subgraph
|
|
"""
|
|
etype_ids = [graph.get_etype_id(etype) for etype in etypes]
|
|
# meta graph is homogeneous graph, still using int64
|
|
meta_src, meta_dst, _ = graph._graph.metagraph.find_edges(
|
|
utils.toindex(etype_ids, "int64")
|
|
)
|
|
rel_graphs = [graph._graph.get_relation_graph(i) for i in etype_ids]
|
|
meta_src = meta_src.tonumpy()
|
|
meta_dst = meta_dst.tonumpy()
|
|
ntypes_invmap = {n: i for i, n in enumerate(set(meta_src) | set(meta_dst))}
|
|
mapped_meta_src = [ntypes_invmap[v] for v in meta_src]
|
|
mapped_meta_dst = [ntypes_invmap[v] for v in meta_dst]
|
|
node_frames = [graph._node_frames[i] for i in ntypes_invmap]
|
|
edge_frames = [graph._edge_frames[i] for i in etype_ids]
|
|
induced_ntypes = [graph._ntypes[i] for i in ntypes_invmap]
|
|
induced_etypes = [
|
|
graph._etypes[i] for i in etype_ids
|
|
] # get the "name" of edge type
|
|
num_nodes_per_induced_type = [
|
|
graph.number_of_nodes(ntype) for ntype in induced_ntypes
|
|
]
|
|
|
|
metagraph = graph_index.from_edge_list(
|
|
(mapped_meta_src, mapped_meta_dst), True
|
|
)
|
|
# num_nodes_per_type should be int64
|
|
hgidx = heterograph_index.create_heterograph_from_relations(
|
|
metagraph,
|
|
rel_graphs,
|
|
utils.toindex(num_nodes_per_induced_type, "int64"),
|
|
)
|
|
hg = DGLGraph(
|
|
hgidx, induced_ntypes, induced_etypes, node_frames, edge_frames
|
|
)
|
|
return hg if output_device is None else hg.to(output_device)
|
|
|
|
|
|
DGLGraph.edge_type_subgraph = utils.alias_func(edge_type_subgraph)
|
|
|
|
#################### Internal functions ####################
|
|
|
|
|
|
def _create_hetero_subgraph(
|
|
parent,
|
|
sgi,
|
|
induced_nodes_or_device,
|
|
induced_edges_or_device,
|
|
store_ids=True,
|
|
):
|
|
"""Internal function to create a subgraph.
|
|
|
|
Parameters
|
|
----------
|
|
parent : DGLGraph
|
|
The parent DGLGraph.
|
|
sgi : HeteroSubgraphIndex
|
|
Subgraph object returned by CAPI.
|
|
induced_nodes_or_device : list[Tensor] or device or None
|
|
Induced node IDs or the device. Will store it as the dgl.NID ndata unless it
|
|
is None, which means the induced node IDs are the same as the parent node IDs.
|
|
If a device is given, the features will be copied to the given device.
|
|
induced_edges_or_device : list[Tensor] or device or None
|
|
Induced edge IDs. Will store it as the dgl.EID ndata unless it
|
|
is None, which means the induced edge IDs are the same as the parent edge IDs.
|
|
If a device is given, the features will be copied to the given device.
|
|
store_ids : bool
|
|
If True and induced_nodes is not None, it will store the raw IDs of the extracted
|
|
nodes in the ``ndata`` of the resulting graph under name ``dgl.NID``.
|
|
If True and induced_edges is not None, it will store the raw IDs of the extracted
|
|
edges in the ``edata`` of the resulting graph under name ``dgl.EID``.
|
|
|
|
Returns
|
|
-------
|
|
DGLGraph
|
|
Graph
|
|
"""
|
|
# (BarclayII) Giving a device argument to induced_nodes_or_device is necessary for
|
|
# UVA subgraphing, where the node features are not sliced but the device changed.
|
|
# Not having this will give us a subgraph on GPU but node features on CPU if we don't
|
|
# relabel the nodes.
|
|
node_frames = utils.extract_node_subframes(
|
|
parent, induced_nodes_or_device, store_ids
|
|
)
|
|
edge_frames = utils.extract_edge_subframes(
|
|
parent, induced_edges_or_device, store_ids
|
|
)
|
|
hsg = DGLGraph(sgi.graph, parent.ntypes, parent.etypes)
|
|
utils.set_new_frames(hsg, node_frames=node_frames, edge_frames=edge_frames)
|
|
return hsg
|
|
|
|
|
|
_init_api("dgl.subgraph")
|