Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Added Random Walks COO convertor and profiling #1531

Merged
merged 36 commits into from
Apr 28, 2021
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
acf15bc
Merge pull request #37 from rapidsai/branch-0.17
aschaffer Nov 30, 2020
a584e0b
Merge pull request #38 from rapidsai/branch-0.18
aschaffer Dec 9, 2020
338b2d4
Merge pull request #39 from rapidsai/branch-0.18
aschaffer Dec 30, 2020
efd7e9a
Merge pull request #40 from rapidsai/branch-0.18
aschaffer Jan 12, 2021
a23ce0d
Merge pull request #41 from rapidsai/branch-0.19
aschaffer Feb 25, 2021
22b32d8
Merge pull request #42 from rapidsai/branch-0.19
aschaffer Mar 1, 2021
e5042fc
Merge pull request #43 from rapidsai/branch-0.19
aschaffer Mar 20, 2021
ca02ee4
Merge pull request #44 from rapidsai/branch-0.19
aschaffer Apr 7, 2021
d6759b8
Merge branch 'branch-0.19' of github.com:rapidsai/cugraph into enh_ex…
aschaffer Apr 8, 2021
4b430e0
Added convertor from paths to COO.
aschaffer Apr 8, 2021
b47879f
Exposed convertor from paths to COO to become callable from Cython.
aschaffer Apr 8, 2021
a028431
Debugging Paths2COO converter.
aschaffer Apr 9, 2021
2b62eaf
Fix 1 for Paths2COO converter.
aschaffer Apr 9, 2021
da1f15c
Fix 2 for Paths2COO converter.
aschaffer Apr 9, 2021
6909b34
Fix for Paths2COO converter.
aschaffer Apr 9, 2021
1e7b92e
Final fix for Paths2COO converter.
aschaffer Apr 9, 2021
e94c858
Tweaked the convertor API to meet some Cython demands.
aschaffer Apr 12, 2021
60b113e
Fixes for the convertor API to match Cython demands.
aschaffer Apr 13, 2021
50f70d6
Cython API for Path2COO.
aschaffer Apr 13, 2021
612d921
Cython EIDirs for Path2COO.
aschaffer Apr 13, 2021
a8e8095
Added Single Path test.
aschaffer Apr 13, 2021
7d33fbb
Added Profiling for RW.
aschaffer Apr 13, 2021
5b5c994
Added Profiling functionality to evaluate average runtime/number_path…
aschaffer Apr 13, 2021
34575f5
Moved RW tests out of experimental into their own dir (sampling).
aschaffer Apr 13, 2021
496b566
Merge branch 'branch-0.20' of github.com:rapidsai/cugraph into enh_ex…
aschaffer Apr 13, 2021
0537756
Moved cuh out of experimental.
aschaffer Apr 14, 2021
28509e2
Addressed code review comments on returning HighResTimer average runt…
aschaffer Apr 14, 2021
a5cbd25
Addressed code review comments on test fixtures.
aschaffer Apr 14, 2021
398a10b
Addressed code review comments on moving Paths2COO convertor out of a…
aschaffer Apr 15, 2021
c0d0c9a
Merge branch 'branch-0.20' of github.com:rapidsai/cugraph into enh_ex…
aschaffer Apr 15, 2021
d5105ae
Converted random_walks profiling from a gtest test binary to a simple…
rlratzel Apr 16, 2021
5a5cb3e
clang format changes.
rlratzel Apr 16, 2021
0cfacec
Merge remote-tracking branch 'upstream/branch-0.20' into enh_ext_rw_p…
rlratzel Apr 16, 2021
8493df3
Using doxygen descriptions for internal functions.
rlratzel Apr 16, 2021
0b681ef
Merge branch 'branch-0.20' of github.com:rapidsai/cugraph into enh_ex…
aschaffer Apr 28, 2021
ce7ba58
Merge branch 'branch-0.20' of github.com:rapidsai/cugraph into enh_ex…
aschaffer Apr 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions cpp/include/algorithms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1280,5 +1280,29 @@ random_walks(raft::handle_t const &handle,
typename graph_t::vertex_type const *ptr_d_start,
index_t num_paths,
index_t max_depth);

/**
* @brief returns the COO format (src_vector, dst_vector) from the random walks (RW)
* paths.
*
* @tparam vertex_t Type of vertex indices.
* @tparam index_t Type used to store indexing and sizes.
* @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and
* handles to various CUDA libraries) to run graph algorithms.
* @param coalesced_sz_v coalesced vertex vector size.
* @param num_paths number of paths.
* @param d_coalesced_v coalesced vertex buffer.
* @param d_sizes paths size buffer.
* @return tuple of (src_vertex_vector, dst_Vertex_vector, path_offsets), where
* path_offsets are the offsets where the COO set of each path starts.
*/
template <typename vertex_t, typename index_t>
std::
tuple<rmm::device_uvector<vertex_t>, rmm::device_uvector<vertex_t>, rmm::device_uvector<index_t>>
convert_paths_to_coo(raft::handle_t const &handle,
aschaffer marked this conversation as resolved.
Show resolved Hide resolved
index_t coalesced_sz_v,
index_t num_paths,
rmm::device_buffer &&d_coalesced_v,
rmm::device_buffer &&d_sizes);
} // namespace experimental
} // namespace cugraph
26 changes: 26 additions & 0 deletions cpp/include/utilities/cython.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,26 @@ struct random_walk_ret_t {
std::unique_ptr<rmm::device_buffer> d_sizes_;
};

// aggregate for random_walks() COO return type
// to be exposed to cython:
//
struct random_walk_coo_t {
aschaffer marked this conversation as resolved.
Show resolved Hide resolved
size_t num_edges_; // total number of COO triplets (for all paths)
size_t num_offsets_; // offsets of where each COO set starts for each path;
// NOTE: this can differ than num_paths_,
// because paths with 0 edges (one vertex)
// don't participate to the COO

std::unique_ptr<rmm::device_buffer>
d_src_; // coalesced set of COO source vertices; |d_src_| = num_edges_
std::unique_ptr<rmm::device_buffer>
d_dst_; // coalesced set of COO destination vertices; |d_dst_| = num_edges_
std::unique_ptr<rmm::device_buffer>
d_weights_; // coalesced set of COO edge weights; |d_weights_| = num_edges_
std::unique_ptr<rmm::device_buffer>
d_offsets_; // offsets where each COO subset for each path starts; |d_offsets_| = num_offsets_
};

// wrapper for renumber_edgelist() return
// (unrenumbering maps, etc.)
//
Expand Down Expand Up @@ -479,6 +499,12 @@ call_random_walks(raft::handle_t const& handle,
edge_t num_paths,
edge_t max_depth);

// convertor from random_walks return type to COO:
//
template <typename vertex_t, typename index_t>
std::unique_ptr<random_walk_coo_t> random_walks_to_coo(raft::handle_t const& handle,
random_walk_ret_t& rw_ret);

// wrapper for shuffling:
//
template <typename vertex_t, typename edge_t, typename weight_t>
Expand Down
193 changes: 193 additions & 0 deletions cpp/src/experimental/random_walks.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <thrust/logical.h>
#include <thrust/remove.h>
#include <thrust/transform.h>
#include <thrust/transform_scan.h>
#include <thrust/tuple.h>

#include <cassert>
Expand Down Expand Up @@ -103,6 +104,12 @@ struct device_const_vector_view {
index_t size_;
};

template <typename value_t>
value_t const* raw_const_ptr(device_const_vector_view<value_t>& dv)
{
return dv.begin();
}

// raft random generator:
// (using upper-bound cached "map"
// giving out_deg(v) for each v in [0, |V|);
Expand Down Expand Up @@ -840,6 +847,156 @@ random_walks_impl(raft::handle_t const& handle,
CUGRAPH_FAIL("Not implemented yet.");
}

// provides conversion to (coalesced) path to COO format:
aschaffer marked this conversation as resolved.
Show resolved Hide resolved
// (which in turn provides an API consistent with egonet)
//
template <typename vertex_t, typename index_t>
struct coo_convertor_t {
coo_convertor_t(raft::handle_t const& handle, index_t num_paths)
: handle_(handle), num_paths_(num_paths)
{
}

std::tuple<device_vec_t<vertex_t>, device_vec_t<vertex_t>, device_vec_t<index_t>> operator()(
device_const_vector_view<vertex_t>& d_coalesced_v,
device_const_vector_view<index_t>& d_sizes) const
{
CUGRAPH_EXPECTS(static_cast<index_t>(d_sizes.size()) == num_paths_, "Invalid size vector.");

auto tupl_fill = fill_stencil(d_sizes);
auto&& d_stencil = std::move(std::get<0>(tupl_fill));
auto total_sz_v = std::get<1>(tupl_fill);
auto&& d_sz_incl_scan = std::move(std::get<2>(tupl_fill));

CUGRAPH_EXPECTS(static_cast<index_t>(d_coalesced_v.size()) == total_sz_v,
"Inconsistent vertex coalesced size data.");

auto src_dst_tpl = gather_pairs(d_coalesced_v, d_stencil, total_sz_v);

auto&& d_src = std::move(std::get<0>(src_dst_tpl));
auto&& d_dst = std::move(std::get<1>(src_dst_tpl));

device_vec_t<index_t> d_sz_w_scan(num_paths_, handle_.get_stream());

// copy vertex path sizes that are > 1:
// (because vertex_path_sz translates
// into edge_path_sz = vertex_path_sz - 1,
// and edge_paths_sz == 0 don't contribute
// anything):
//
auto new_end_it =
thrust::copy_if(rmm::exec_policy(handle_.get_stream())->on(handle_.get_stream()),
d_sizes.begin(),
d_sizes.end(),
d_sz_w_scan.begin(),
[] __device__(auto sz_value) { return sz_value > 1; });

// resize to new_end:
//
d_sz_w_scan.resize(thrust::distance(d_sz_w_scan.begin(), new_end_it), handle_.get_stream());

// get paths' edge number exclusive scan
// by transforming paths' vertex numbers that
// are > 1, via tranaformation:
// edge_path_sz = (vertex_path_sz-1):
//
thrust::transform_exclusive_scan(
rmm::exec_policy(handle_.get_stream())->on(handle_.get_stream()),
d_sz_w_scan.begin(),
d_sz_w_scan.end(),
d_sz_w_scan.begin(),
[] __device__(auto sz) { return sz - 1; },
index_t{0},
thrust::plus<index_t>{});

return std::make_tuple(std::move(d_src), std::move(d_dst), std::move(d_sz_w_scan));
}

std::tuple<device_vec_t<int>, index_t, device_vec_t<index_t>> fill_stencil(
device_const_vector_view<index_t>& d_sizes) const
{
device_vec_t<index_t> d_scan(num_paths_, handle_.get_stream());
thrust::inclusive_scan(rmm::exec_policy(handle_.get_stream())->on(handle_.get_stream()),
d_sizes.begin(),
d_sizes.end(),
d_scan.begin());

index_t total_sz{0};
CUDA_TRY(cudaMemcpy(
&total_sz, raw_ptr(d_scan) + num_paths_ - 1, sizeof(index_t), cudaMemcpyDeviceToHost));

device_vec_t<int> d_stencil(total_sz, handle_.get_stream());

// initialize stencil to all 1's:
//
thrust::copy_n(rmm::exec_policy(handle_.get_stream())->on(handle_.get_stream()),
thrust::make_constant_iterator<int>(1),
d_stencil.size(),
d_stencil.begin());

// set to 0 entries positioned at inclusive_scan(sizes[]),
// because those are path "breakpoints", where a path end
// and the next one starts, hence there cannot be an edge
// between a path ending vertex and next path starting vertex;
//
thrust::scatter(rmm::exec_policy(handle_.get_stream())->on(handle_.get_stream()),
thrust::make_constant_iterator(0),
thrust::make_constant_iterator(0) + num_paths_,
d_scan.begin(),
d_stencil.begin());

return std::make_tuple(std::move(d_stencil), total_sz, std::move(d_scan));
}

std::tuple<device_vec_t<vertex_t>, device_vec_t<vertex_t>> gather_pairs(
device_const_vector_view<vertex_t>& d_coalesced_v,
device_vec_t<int> const& d_stencil,
index_t total_sz_v) const
{
auto total_sz_w = total_sz_v - num_paths_;
device_vec_t<index_t> valid_src_indx(total_sz_w, handle_.get_stream());

// generate valid vertex src indices,
// which is any index in {0,...,total_sz_v - 2}
// provided the next index position; i.e., (index+1),
// in stencil is not 0; (if it is, there's no "next"
// or dst index, because the path has ended);
//
thrust::copy_if(rmm::exec_policy(handle_.get_stream())->on(handle_.get_stream()),
thrust::make_counting_iterator<index_t>(0),
thrust::make_counting_iterator<index_t>(total_sz_v - 1),
valid_src_indx.begin(),
[ptr_d_stencil = raw_const_ptr(d_stencil)] __device__(auto indx) {
auto dst_indx = indx + 1;
return ptr_d_stencil[dst_indx] == 1;
});

device_vec_t<vertex_t> d_src_v(total_sz_w, handle_.get_stream());
device_vec_t<vertex_t> d_dst_v(total_sz_w, handle_.get_stream());

// construct pair of src[], dst[] by gathering
// from d_coalesced_v all pairs
// at entries (valid_src_indx, valid_src_indx+1),
// where the set of valid_src_indx was
// generated at the previous step;
//
thrust::transform(
rmm::exec_policy(handle_.get_stream())->on(handle_.get_stream()),
valid_src_indx.begin(),
valid_src_indx.end(),
thrust::make_zip_iterator(thrust::make_tuple(d_src_v.begin(), d_dst_v.begin())), // start_zip
[ptr_d_vertex = raw_const_ptr(d_coalesced_v)] __device__(auto indx) {
return thrust::make_tuple(ptr_d_vertex[indx], ptr_d_vertex[indx + 1]);
});

return std::make_tuple(std::move(d_src_v), std::move(d_dst_v));
}

private:
raft::handle_t const& handle_;
index_t num_paths_;
};

} // namespace detail

/**
Expand Down Expand Up @@ -883,5 +1040,41 @@ random_walks(raft::handle_t const& handle,
std::move(std::get<1>(quad_tuple)),
std::move(std::get<2>(quad_tuple)));
}

/**
* @brief returns the COO format (src_vector, dst_vector) from the random walks (RW)
* paths.
*
* @tparam vertex_t Type of vertex indices.
* @tparam index_t Type used to store indexing and sizes.
* @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and
* handles to various CUDA libraries) to run graph algorithms.
* @param coalesced_sz_v coalesced vertex vector size.
* @param num_paths number of paths.
* @param d_coalesced_v coalesced vertex buffer.
* @param d_sizes paths size buffer.
* @return tuple of (src_vertex_vector, dst_Vertex_vector, path_offsets), where
* path_offsets are the offsets where the COO set of each path starts.
*/
template <typename vertex_t, typename index_t>
std::
tuple<rmm::device_uvector<vertex_t>, rmm::device_uvector<vertex_t>, rmm::device_uvector<index_t>>
convert_paths_to_coo(raft::handle_t const& handle,
index_t coalesced_sz_v,
index_t num_paths,
rmm::device_buffer&& d_coalesced_v,
rmm::device_buffer&& d_sizes)
{
detail::coo_convertor_t<vertex_t, index_t> to_coo(handle, num_paths);

detail::device_const_vector_view<vertex_t> d_v_view(
static_cast<vertex_t const*>(d_coalesced_v.data()), coalesced_sz_v);

detail::device_const_vector_view<index_t> d_sz_view(static_cast<index_t const*>(d_sizes.data()),
num_paths);

return to_coo(d_v_view, d_sz_view);
}

} // namespace experimental
} // namespace cugraph
24 changes: 24 additions & 0 deletions cpp/src/sampling/random_walks.cu
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,30 @@ template std::
int64_t const* ptr_d_start,
int64_t num_paths,
int64_t max_depth);

template std::
tuple<rmm::device_uvector<int32_t>, rmm::device_uvector<int32_t>, rmm::device_uvector<int32_t>>
convert_paths_to_coo(raft::handle_t const& handle,
int32_t coalesced_sz_v,
int32_t num_paths,
rmm::device_buffer&& d_coalesced_v,
rmm::device_buffer&& d_sizes);

template std::
tuple<rmm::device_uvector<int32_t>, rmm::device_uvector<int32_t>, rmm::device_uvector<int64_t>>
convert_paths_to_coo(raft::handle_t const& handle,
int64_t coalesced_sz_v,
int64_t num_paths,
rmm::device_buffer&& d_coalesced_v,
rmm::device_buffer&& d_sizes);

template std::
tuple<rmm::device_uvector<int64_t>, rmm::device_uvector<int64_t>, rmm::device_uvector<int64_t>>
convert_paths_to_coo(raft::handle_t const& handle,
int64_t coalesced_sz_v,
int64_t num_paths,
rmm::device_buffer&& d_coalesced_v,
rmm::device_buffer&& d_sizes);
//}
} // namespace experimental
} // namespace cugraph
30 changes: 30 additions & 0 deletions cpp/src/utilities/cython.cu
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,27 @@ call_random_walks(raft::handle_t const& handle,
}
}

template <typename vertex_t, typename index_t>
std::unique_ptr<random_walk_coo_t> random_walks_to_coo(raft::handle_t const& handle,
random_walk_ret_t& rw_tri)
{
auto triplet = cugraph::experimental::convert_paths_to_coo<vertex_t, index_t>(
handle,
static_cast<index_t>(rw_tri.coalesced_sz_v_),
static_cast<index_t>(rw_tri.num_paths_),
std::move(*rw_tri.d_coalesced_v_),
std::move(*rw_tri.d_sizes_));

random_walk_coo_t rw_coo{std::get<0>(triplet).size(),
std::get<2>(triplet).size(),
std::make_unique<rmm::device_buffer>(std::get<0>(triplet).release()),
std::make_unique<rmm::device_buffer>(std::get<1>(triplet).release()),
std::move(rw_tri.d_coalesced_w_), // pass-through
std::make_unique<rmm::device_buffer>(std::get<2>(triplet).release())};

return std::make_unique<random_walk_coo_t>(std::move(rw_coo));
}

// Wrapper for calling SSSP through a graph container
template <typename vertex_t, typename weight_t>
void call_sssp(raft::handle_t const& handle,
Expand Down Expand Up @@ -1228,6 +1249,15 @@ template std::unique_ptr<random_walk_ret_t> call_random_walks<int64_t, int64_t>(
int64_t num_paths,
int64_t max_depth);

template std::unique_ptr<random_walk_coo_t> random_walks_to_coo<int32_t, int32_t>(
raft::handle_t const& handle, random_walk_ret_t& rw_tri);

template std::unique_ptr<random_walk_coo_t> random_walks_to_coo<int32_t, int64_t>(
raft::handle_t const& handle, random_walk_ret_t& rw_tri);

template std::unique_ptr<random_walk_coo_t> random_walks_to_coo<int64_t, int64_t>(
raft::handle_t const& handle, random_walk_ret_t& rw_tri);

template void call_sssp(raft::handle_t const& handle,
graph_container_t const& graph_container,
int32_t* identifiers,
Expand Down
11 changes: 11 additions & 0 deletions cpp/src/utilities/high_res_timer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ class HighResTimer {
timers.clear();
}

double average_runtime(std::string const &label)
aschaffer marked this conversation as resolved.
Show resolved Hide resolved
{
auto it = timers.find(label);
if (it != timers.end()) {
return (static_cast<double>(it->second.second) / (1000000.0 * it->second.first));
} else {
std::cerr << "ERROR: timing label: " << label << "not found.";
aschaffer marked this conversation as resolved.
Show resolved Hide resolved
return 0.0;
}
}

private:
std::map<std::string, std::pair<int, int64_t>> timers;
std::string open_label; // should probably be a stack...
Expand Down
Loading