Skip to content

Temporal sampling implementation #4994

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

Open
wants to merge 33 commits into
base: branch-25.08
Choose a base branch
from

Conversation

ChuckHastings
Copy link
Collaborator

@ChuckHastings ChuckHastings commented Mar 20, 2025

Temporal sampling implementation. Sampling considers the time stamp of edges, if we arrive at a vertex v with timestamp t1, then when we depart from that vertex to continue sampling we only consider edges that occur after time t1.

PR includes C++ implementation and tests.

This significantly increases C++ compile time. We'll address this during 25.08.

Copy link

copy-pr-bot bot commented Mar 20, 2025

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@ChuckHastings ChuckHastings self-assigned this Mar 20, 2025
@ChuckHastings ChuckHastings added improvement Improvement / enhancement to an existing function non-breaking Non-breaking change and removed cuGraph CMake labels Mar 20, 2025
Copy link
Contributor

@seunghwak seunghwak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about efficient implementation of temporal sampling especially considering that some seed vertices can be reached from multiple different paths and we need to apply multiple different temporal windows for the same seed vertex.

This can lead to many vertex partitions especially for power-law graphs.

And applying & creating graph-wise temporal mask can be pretty expensive if we need to do this many times.

We can apply a graph-wise temporal mask to set temporal window including the lower and upper bound of the start/end times for the entire set of seeds in multiple batches.

For a seed specific time window, I think adjusting bias values will lead to more efficient implementation.

We can tag a seed vertex with a time-stamp (https://github.com/rapidsai/cugraph/blob/branch-25.04/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh#L1092C28-L1092C72).

And when we set the bias value (https://github.com/rapidsai/cugraph/blob/branch-25.04/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh#L1096), we can set the bias value to 0 if the edge is outside the seed specific time window.

I think this can lead to more efficient implementation than the current approach.

What do you think about this?

@seunghwak
Copy link
Contributor

And for uniform sampling, we may use a uniform sampling primitive for seeds that appear no more than once and use a biased sampling primitive for seeds that appear two or more times.

@ChuckHastings
Copy link
Collaborator Author

I thought about efficient implementation of temporal sampling especially considering that some seed vertices can be reached from multiple different paths and we need to apply multiple different temporal windows for the same seed vertex.

This can lead to many vertex partitions especially for power-law graphs.

And applying & creating graph-wise temporal mask can be pretty expensive if we need to do this many times.

We can apply a graph-wise temporal mask to set temporal window including the lower and upper bound of the start/end times for the entire set of seeds in multiple batches.

For a seed specific time window, I think adjusting bias values will lead to more efficient implementation.

We can tag a seed vertex with a time-stamp (https://github.com/rapidsai/cugraph/blob/branch-25.04/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh#L1092C28-L1092C72).

And when we set the bias value (https://github.com/rapidsai/cugraph/blob/branch-25.04/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh#L1096), we can set the bias value to 0 if the edge is outside the seed specific time window.

I think this can lead to more efficient implementation than the current approach.

What do you think about this?

So something akin to what node2vec does... return a bias of 0 if the edge time is invalid, return a bias of 1 if the edge time is valid. Because we're operating on the tagged vertex, each vertex would have its own timestamp... therefore its own computed bias.

If my interpretation is correct, I think that would be a much simpler implementation and would probably result in significantly better performance in the cases where we end up with a high degree vertex that appears multiple times in the frontier.

@seunghwak
Copy link
Contributor

erpretation is correct, I think that would be a much simpler implementation and would probably result in significantly better performance in the cases w

Yes, your interpretation is correct. I agree that this will be simpler & faster. For uniform sampling and to avoid the overhead of evaluating bias for every edge, we can use just a default uniform sampling for seeds that appear only once, and use bias values & tagging for seeds that appear more than once.

@alexbarghi-nv
Copy link
Member

@ChuckHastings what is the status of this PR? Will this be completed in release 25.06?

Copy link
Contributor

@seunghwak seunghwak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (besides few comments about minor cosmetic issues)

template <typename vertex_t, typename value_t>
std::tuple<rmm::device_uvector<vertex_t>, rmm::device_uvector<value_t>>
template <typename vertex_t, typename value_vector_t>
std::tuple<rmm::device_uvector<vertex_t>, value_vector_t>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just use dataframe_buffer_type_t (https://github.com/rapidsai/cugraph/blob/branch-25.06/cpp/include/cugraph/utilities/dataframe_buffer.hpp#L91) here.

Just replacing rmm::device_uvector<value_t> with dataframe_buffer_type_t<value_t> will work.

Comment on lines 207 to 211
template <typename... Ts, std::size_t... Is>
auto view_concat_impl(std::tuple<Ts...> const& tuple, std::index_sequence<Is...>)
{
return view_concat(std::get<Is>(tuple)...);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A convention is to place the detail namespace code at the beginning of the file.

{
static_assert(is_std_tuple_of_arithmetic_spans<std::remove_cv_t<BufferType>>::value);
if constexpr (is_std_tuple_of_arithmetic_spans<std::remove_cv_t<BufferType>>::value) {
return std::get<0>(buffer).size();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this function? Can't the function above cover std::tuple of std::span as well?

Comment on lines 116 to 120
std::conditional_t<std::is_same_v<edge_properties_t, cuda::std::nullopt_t>,
thrust::tuple<>,
std::conditional_t<std::is_arithmetic_v<edge_properties_t>,
thrust::tuple<edge_properties_t>,
edge_properties_t>>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this edge_properties_tup_type defined above? We can just use it here.

}
}

// FIXME: Duplicated across files...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we move this to detail/sampling_utils.hpp to avoid duplication?

@@ -16,9 +16,11 @@

#pragma once

#include "cugraph/utilities/shuffle_comm.cuh"
#include "prims/update_edge_src_dst_property.cuh" // ??
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be from a previous PR but why ?? If we are not using this, can't we delete?


edge_src_property_t<edge_t, edge_time_t, false> edge_src_times(handle, graph_view);

#if 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better delete the dead code.


#if 0
// FIXME: This call to update_edge_src_property seems like what I want, but it
// doesn't work.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why? What happened with this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variation of the call is only used in a few places. MG SSSP, and Random Walks are the only places I see it.

Not sure if it's broken there and we haven't noticed, or if I'm doing something wrong here. But I printed the relevant contents of the arrays and they were incorrect. Since I use them as indices into device arrays, I could clearly see that the wrong values affected the output.

I spent a little time trying to diagnose, but didn't have time to really dig in. It's possible I've done something incorrect in the setup, but setting do_expensive_check didn't reveal anything wrong.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I reproduce this? I can dig in to see what's going awry here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try and create a reproducer for you.

template <typename vertex_t, typename value_t>
std::tuple<rmm::device_uvector<vertex_t>, rmm::device_uvector<value_t>>
template <typename vertex_t, typename value_vector_t>
std::tuple<rmm::device_uvector<vertex_t>, value_vector_t>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use dataframe_buffer_type_t<value_t>

@ChuckHastings ChuckHastings requested a review from a team as a code owner May 15, 2025 22:49
@ChuckHastings ChuckHastings requested a review from bdice May 15, 2025 22:49
@ChuckHastings ChuckHastings requested a review from a team as a code owner May 16, 2025 02:52
@github-actions github-actions bot added the ci label May 16, 2025
@github-actions github-actions bot removed the ci label May 23, 2025
@ChuckHastings ChuckHastings removed request for a team and bdice May 23, 2025 20:36
@github-actions github-actions bot added the ci label May 25, 2025
@ChuckHastings ChuckHastings changed the base branch from branch-25.06 to branch-25.08 May 29, 2025 20:51
@github-actions github-actions bot removed the ci label Jun 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CMake cuGraph improvement Improvement / enhancement to an existing function non-breaking Non-breaking change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants