Skip to content
Draft
Changes from 3 commits
Commits
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
123 changes: 86 additions & 37 deletions cpp/include/cugraph/graph_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,15 +412,40 @@ decompress_to_edgelist(

/**
* @ingroup graph_functions_cpp
* @brief Symmetrize edgelist.

* @brief Identify if an edge property is part of the uniqueness of the edge, or just a value to be
combined.
*/
enum class edge_key_selector_t {
KEY, /** This property is part of the uniqueness key of the edge. */
VALUE /** This property is a value to be combined. */
};

/**
* @ingroup graph_functions_cpp
* @brief Rule for combining edge properties when merging reciprocal edges.
*/
enum class edge_value_combining_rule_t {
SUM, /** Sum the values of the two edge properties. */
MAX, /** Take the maximum of the values of the two edge properties. */
MIN, /** Take the minimum of the values of the two edge properties. */
MEAN, /** Take the mean of the values of the two edge properties. */
FIRST, /** Use the value of the first edge property. */
LAST, /** Use the value of the last edge property. */
ANY, /** Use the value of any edge property. */
NONE /** Do not combine the values. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is just to keep the original values, right? Should we explain this in little more detail?

I initially thought we don't need NONE here as we now have edge_key_selector_t. But now I think this is still necessary as we may consider two different edges with the matching source/destination pairs as reciprocal but in some cases, we don't want to combine there edge properties. This may not be clear to everyone from the beginning.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'll add some text to clarify the different use cases.

};

/**
* @ingroup graph_functions_cpp
* @brief Add reciprocal edges and merge the edges. This was formerly called symmetrize_edgelist
* with reciprocal set to false.
*
* The input edge list is replicated as reciprocal edges. Then the original edges and the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am wondering we should use "reciprocal" here or use "inverse" instead. I am not sure what "reciprocal" exactly means. Some quick search says it is a pair of edges going in both directions between a pair of vertices.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm fine switching to inverse here if that's more intuitive to you. I suppose by the definition you found we're really creating reciprocal edges here by adding the inverse edge. So that's probably more correct.

* reciprocal edges are merged based on the provided @p edge_key_selector and @p
* edge_value_combining_rule.
*
* @tparam vertex_t Type of vertex identifiers. Needs to be an integral type.
* @tparam edge_t Type of edge identifiers. Needs to be an integral type.
* @tparam weight_t Type of edge weights. Needs to be a floating point type.
* @tparam edge_type_t Type of edge type identifiers. Needs to be an integral type.
* @tparam edge_time_t Type of edge time. Needs to be an integral type.
* @tparam store_transposed Flag indicating whether to use sources (if false) or destinations (if
* true) as major indices in storing edges using a 2D sparse matrix.
* @tparam multi_gpu Flag indicating whether template instantiation should target single-GPU (false)
* or multi-GPU (true).
*
Expand All @@ -430,39 +455,63 @@ decompress_to_edgelist(
* compute_gpu_id_from_ext_edge_endpoints_t to every edge should return the local GPU ID for this
* function to work (edges should be pre-shuffled).
* @param edgelist_dsts Vector of edge destination vertex IDs.
* @param edgelist_weights Vector of edge weights.
* @param edgelist_edge_ids Vector of edge ids
* @param edgelist_edge_types Vector of edge types
* @param edgelist_edge_start_times Vector of edge start times
* @param edgelist_edge_end_times Vector of edge end times
* @param reciprocal Flag indicating whether to keep (if set to `false`) or discard (if set to
* `true`) edges that appear only in one direction.
* @return Tuple of symmetrized sources, destinations, optional weights, optional edge ids, optional
* edge types, optional edge start times and optional edge end times
* @param edgelist_edge_properties Vector of edge properties.
* @param edge_key_selector Vector of selectors for selecting the key of the edge properties.
* @param edge_value_combining_rule Vector of rules for combining edge values when merging
* reciprocal edges. reciprocal edges.
* @param store_transposed Flag indicating whether to use sources (if false) or destinations (if
* true) as major indices in storing edges using a 2D sparse matrix.
* @return Tuple of symmetrized sources, destinations, and edge properties.
*/
template <typename vertex_t,
typename edge_t,
typename weight_t,
typename edge_type_t,
typename edge_time_t,
bool store_transposed,
bool multi_gpu>
template <typename vertex_t, bool multi_gpu>
std::tuple<rmm::device_uvector<vertex_t>,
rmm::device_uvector<vertex_t>,
std::optional<rmm::device_uvector<weight_t>>,
std::optional<rmm::device_uvector<edge_t>>,
std::optional<rmm::device_uvector<edge_type_t>>,
std::optional<rmm::device_uvector<edge_time_t>>,
std::optional<rmm::device_uvector<edge_time_t>>>
symmetrize_edgelist(raft::handle_t const& handle,
rmm::device_uvector<vertex_t>&& edgelist_srcs,
rmm::device_uvector<vertex_t>&& edgelist_dsts,
std::optional<rmm::device_uvector<weight_t>>&& edgelist_weights,
std::optional<rmm::device_uvector<edge_t>>&& edgelist_edge_ids,
std::optional<rmm::device_uvector<edge_type_t>>&& edgelist_edge_types,
std::optional<rmm::device_uvector<edge_time_t>>&& edgelist_edge_start_times,
std::optional<rmm::device_uvector<edge_time_t>>&& edgelist_edge_end_times,
bool reciprocal);
std::vector<arithmetic_device_uvector_t>>
add_reciprocals_and_merge_edge_tuples(
raft::handle_t const& handle,
rmm::device_uvector<vertex_t>&& edgelist_srcs,
rmm::device_uvector<vertex_t>&& edgelist_dsts,
std::vector<arithmetic_device_uvector_t>&& edgelist_edge_properties,
std::vector<edge_key_selector_t> const& edge_key_selector,
std::vector<edge_value_combining_rule_t> const& edge_value_combining_rule,
bool store_transposed);

/**
* @ingroup graph_functions_cpp
* @brief Remove non-reciprocal edges from the edgelist. This was formerly called
* symmetrize_edgelist with reciprocal set to true.
*
* The input edge list scanned, each edge tuple that does not have a matching reciprocal edge is
* removed. @p edge_key_selector is used to determine if an edge property is part of the uniqueness
* key of the edge. If it is part of the uniqueness key, the edge tuple is removed if it does not
* have a matching reciprocal edge. If it is not part of the uniqueness key, the edge property is
* ignored in the comparison.
*
* @tparam vertex_t Type of vertex identifiers. Needs to be an integral type.
* @tparam multi_gpu Flag indicating whether template instantiation should target single-GPU (false)
* or multi-GPU (true).
* @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and
* handles to various CUDA libraries) to run graph algorithms.
* @param edgelist_srcs Vector of edge source vertex IDs. If multi-GPU, applying the
* compute_gpu_id_from_ext_edge_endpoints_t to every edge should return the local GPU ID for this
* function to work (edges should be pre-shuffled).
* @param edgelist_dsts Vector of edge destination vertex IDs.
* @param edgelist_edge_properties Vector of edge properties.
* @param edge_key_selector Vector of selectors for selecting the key of the edge properties.
* @param store_transposed Flag indicating whether to use sources (if false) or destinations (if
* true) as major indices in storing edges using a 2D sparse matrix.
* @return Tuple of non-reciprocal sources, destinations, and edge properties.
*/
template <typename vertex_t, bool multi_gpu>
std::tuple<rmm::device_uvector<vertex_t>,
rmm::device_uvector<vertex_t>,
std::vector<arithmetic_device_uvector_t>>
drop_nonreciprocal_edge_tuples(raft::handle_t const& handle,
rmm::device_uvector<vertex_t>&& edgelist_srcs,
rmm::device_uvector<vertex_t>&& edgelist_dsts,
std::vector<arithmetic_device_uvector_t>&& edgelist_edge_properties,
std::vector<edge_key_selector_t> const& edge_key_selector,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We don't need std::vector<edge_value_combining_rule_t> const& edge_value_combining_rule here? If we have a reciprocal edges, are there no use-cases we need to combine edge properties? (e.g. update the edge weights to the average)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I suppose we do. I hadn't considered that case. But if we have (1,2,1.0) and (2,1,2.0) then we would need to have the edge_value_combining_rule_t for that edge property to determine what value to assign to the pair of edges. In that case NONE would leave the edges unmodified (and different in each direction), ANY would arbitrarily pick one of them to make them match.

bool store_transposed);

/**
* @ingroup graph_functions_cpp
Expand Down