dmlc--dgl
bf264d00fe
* adding LABOR sampling * add ladies and pladies samplers * fix compile error after rebase * add reference for ladies sampler * Improve ladies implementation. * weighted labor sampling initial implementation draft fix indentation and small bug in ladies script * importance_sampling currently doesn't work with weights * fix weighted importance sampling * move labor example into its own folder * lint fixes * Improve documentation * remove examples from the main PR * fix linting by not using c++17 features * fix documentation of labor_sampler.py * update documentation for labor.py * reformat the labor.py file with black * fix linting errors * replace exception use with if * fix typo in error comment * fixing win64 build for ci * fixing weighted implementation, works now. * fix bug in the weighted case and importance_sampling==0 * address part of the reviews * remove unused code paths from cuda * remove unused code path from cpu side * remove extra features of labor making use of random seed. * fix exclude_edges bug * remove pcg and seed logic from cpu implementation, seed logic should still work for cuda. * minor style change * refactor CPU implementation, take out the importance_sampling probability computation into a function. * improve CUDAWorkspaceAllocator * refactor importance_sampling part out to a function * minor optimization * fix linting issue * Revert "remove pcg and seed logic from cpu implementation, seed logic should still work for cuda." This reverts commit c250e07ac6d7e13f57e79e8a2c2f098d777378c2. * Revert "remove extra features of labor making use of random seed." This reverts commit 7f99034353080308f4783f27d9a08bea343fb796. * fix the documentation * disable NIDs * improve the documentation in the code * use the stream argument in pcg32 instead of skipping ahead t times, can discard the use of hashmap now since it is faster this way. * fix linting issue * address another round of reviews * further optimize CPU LABOR sampling implementation * fix linting error * update the comment * reformat * rename and rephrase comment * fix formatting according to new linting specs * fix compile error due to renaming, fix linting. * lint * rename DGLHeteroGraph to DGLGraph to match master * replace other occurrences of DGLHeteroGraph to DGLGraph Co-authored-by: Muhammed Fatih BALIN <m.f.balin@gmail.com> Co-authored-by: Kaan Sancak <kaansnck@gmail.com> Co-authored-by: Quan Gan <coin2028@hotmail.com>
88 行
3.9 KiB
Python
88 行
3.9 KiB
Python
"""Sampling utilities"""
|
|
from collections.abc import Mapping
|
|
import numpy as np
|
|
|
|
from ..utils import recursive_apply, recursive_apply_pair
|
|
from ..base import EID
|
|
from .. import backend as F
|
|
from .. import transforms, utils
|
|
|
|
def _locate_eids_to_exclude(frontier_parent_eids, exclude_eids):
|
|
"""Find the edges whose IDs in parent graph appeared in exclude_eids.
|
|
|
|
Note that both arguments are numpy arrays or numpy dicts.
|
|
"""
|
|
if not isinstance(frontier_parent_eids, Mapping):
|
|
return np.isin(frontier_parent_eids, exclude_eids).nonzero()[0]
|
|
result = {}
|
|
for k, v in frontier_parent_eids.items():
|
|
if k in exclude_eids:
|
|
result[k] = np.isin(v, exclude_eids[k]).nonzero()[0]
|
|
return recursive_apply(result, F.zerocopy_from_numpy)
|
|
|
|
class EidExcluder(object):
|
|
"""Class that finds the edges whose IDs in parent graph appeared in exclude_eids.
|
|
|
|
The edge IDs can be both CPU and GPU tensors.
|
|
"""
|
|
def __init__(self, exclude_eids):
|
|
device = None
|
|
if isinstance(exclude_eids, Mapping):
|
|
for _, v in exclude_eids.items():
|
|
if device is None:
|
|
device = F.context(v)
|
|
break
|
|
else:
|
|
device = F.context(exclude_eids)
|
|
self._exclude_eids = None
|
|
self._filter = None
|
|
|
|
if device == F.cpu():
|
|
# TODO(nv-dlasalle): Once Filter is implemented for the CPU, we
|
|
# should just use that irregardless of the device.
|
|
self._exclude_eids = (
|
|
recursive_apply(exclude_eids, F.zerocopy_to_numpy)
|
|
if exclude_eids is not None else None)
|
|
else:
|
|
self._filter = recursive_apply(exclude_eids, utils.Filter)
|
|
|
|
def _find_indices(self, parent_eids):
|
|
""" Find the set of edge indices to remove.
|
|
"""
|
|
if self._exclude_eids is not None:
|
|
parent_eids_np = recursive_apply(parent_eids, F.zerocopy_to_numpy)
|
|
return _locate_eids_to_exclude(parent_eids_np, self._exclude_eids)
|
|
else:
|
|
assert self._filter is not None
|
|
func = lambda x, y: x.find_included_indices(y)
|
|
return recursive_apply_pair(self._filter, parent_eids, func)
|
|
|
|
def __call__(self, frontier, weights=None):
|
|
parent_eids = frontier.edata[EID]
|
|
located_eids = self._find_indices(parent_eids)
|
|
|
|
if not isinstance(located_eids, Mapping):
|
|
# (BarclayII) If frontier already has a EID field and located_eids is empty,
|
|
# the returned graph will keep EID intact. Otherwise, EID will change
|
|
# to the mapping from the new graph to the old frontier.
|
|
# So we need to test if located_eids is empty, and do the remapping ourselves.
|
|
if len(located_eids) > 0:
|
|
frontier = transforms.remove_edges(
|
|
frontier, located_eids, store_ids=True)
|
|
if weights is not None and weights[0].shape[0] == frontier.num_edges():
|
|
weights[0] = F.gather_row(weights[0], frontier.edata[EID])
|
|
frontier.edata[EID] = F.gather_row(parent_eids, frontier.edata[EID])
|
|
else:
|
|
# (BarclayII) remove_edges only accepts removing one type of edges,
|
|
# so I need to keep track of the edge IDs left one by one.
|
|
new_eids = parent_eids.copy()
|
|
for i, (k, v) in enumerate(located_eids.items()):
|
|
if len(v) > 0:
|
|
frontier = transforms.remove_edges(
|
|
frontier, v, etype=k, store_ids=True)
|
|
new_eids[k] = F.gather_row(parent_eids[k], frontier.edges[k].data[EID])
|
|
if weights is not None and weights[i].shape[0] == frontier.num_edges(k):
|
|
weights[i] = F.gather_row(weights[i], frontier.edges[k].data[EID])
|
|
frontier.edata[EID] = new_eids
|
|
return frontier if weights is None else (frontier, weights)
|