Skip to content

Commit

Permalink
Add test to reproduce issue with double weights, fix issue (graph cre… (
Browse files Browse the repository at this point in the history
rapidsai#2305)

@alexbarghi-nv discovered issue with creating graphs through the C API with double weights.

Added CAPI SSSP test which demonstrates the error.  Added fix to the error in the graph creation logic (both SG and MG).

Authors:
  - Chuck Hastings (https://github.com/ChuckHastings)

Approvers:
  - Alex Barghi (https://github.com/alexbarghi-nv)
  - Seunghwa Kang (https://github.com/seunghwak)

URL: rapidsai#2305
  • Loading branch information
ChuckHastings authored May 26, 2022
1 parent a8f7f76 commit b0c6a9e
Show file tree
Hide file tree
Showing 9 changed files with 440 additions and 42 deletions.
9 changes: 7 additions & 2 deletions cpp/include/cugraph_c/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ typedef struct {
* @param [in] properties Properties of the constructed graph
* @param [in] src Device array containing the source vertex ids.
* @param [in] dst Device array containing the destination vertex ids
* @param [in] weights Device array containing the edge weights
* @param [in] weights Device array containing the edge weights. Note that an unweighted
* graph can be created by passing weights == NULL.
* @param [in] store_transposed If true create the graph initially in transposed format
* @param [in] renumber If true, renumber vertices to make an efficient data structure.
* If false, do not renumber. Renumbering is required if the vertices are not sequential
Expand Down Expand Up @@ -82,7 +83,11 @@ void cugraph_sg_graph_free(cugraph_graph_t* graph);
* @param [in] properties Properties of the constructed graph
* @param [in] src Device array containing the source vertex ids
* @param [in] dst Device array containing the destination vertex ids
* @param [in] weights Device array containing the edge weights
* @param [in] weights Device array containing the edge weights. Note that an unweighted
* graph can be created by passing weights == NULL. If a weighted
* graph is to be created, the weights device array should be created
* on each rank, but the pointer can be NULL and the size 0
* if there are no inputs provided by this rank
* @param [in] store_transposed If true create the graph initially in transposed format
* @param [in] num_edges Number of edges
* @param [in] check If true, do expensive checks to validate the input data
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/c_api/graph_mg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ extern "C" cugraph_error_code_t cugraph_mg_graph_create(
"Invalid input arguments: src type != dst type.",
*error);

CAPI_EXPECTS(!weights || (p_weights->size_ == p_src->size_),
CAPI_EXPECTS((weights == nullptr) || (p_weights->size_ == p_src->size_),
CUGRAPH_INVALID_INPUT,
"Invalid input arguments: src size != weights size.",
*error);
Expand All @@ -225,7 +225,7 @@ extern "C" cugraph_error_code_t cugraph_mg_graph_create(
edge_type = data_type_id_t::INT64;
}

if (!weights) {
if (weights != nullptr) {
weight_type = p_weights->type_;
} else {
weight_type = data_type_id_t::FLOAT32;
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/c_api/graph_sg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ extern "C" cugraph_error_code_t cugraph_sg_graph_create(
"Invalid input arguments: src type != dst type.",
*error);

CAPI_EXPECTS(!weights || (p_weights->size_ == p_src->size_),
CAPI_EXPECTS((weights == nullptr) || (p_weights->size_ == p_src->size_),
CUGRAPH_INVALID_INPUT,
"Invalid input arguments: src size != weights size.",
*error);
Expand All @@ -218,7 +218,7 @@ extern "C" cugraph_error_code_t cugraph_sg_graph_create(
edge_type = data_type_id_t::INT64;
}

if (!weights) {
if (weights != nullptr) {
weight_type = p_weights->type_;
} else {
weight_type = data_type_id_t::FLOAT32;
Expand Down
13 changes: 13 additions & 0 deletions cpp/tests/c_api/c_test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ int run_sg_test(int (*test)(), const char* test_name);
#define RUN_TEST(test_name) run_sg_test(test_name, #test_name)

int nearlyEqual(float a, float b, float epsilon);
int nearlyEqualDouble(double a, double b, double epsilon);

int create_test_graph(const cugraph_resource_handle_t* p_handle,
int32_t* h_src,
int32_t* h_dst,
Expand All @@ -60,6 +62,17 @@ int create_test_graph(const cugraph_resource_handle_t* p_handle,
cugraph_graph_t** p_graph,
cugraph_error_t** ret_error);

int create_test_graph_double(const cugraph_resource_handle_t* p_handle,
int32_t* h_src,
int32_t* h_dst,
double* h_wgt,
size_t num_edges,
bool_t store_transposed,
bool_t renumber,
bool_t is_symmetric,
cugraph_graph_t** p_graph,
cugraph_error_t** ret_error);

void* create_raft_handle(int prows);
void free_raft_handle(void* raft_handle);

Expand Down
119 changes: 109 additions & 10 deletions cpp/tests/c_api/mg_sssp_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,19 @@

typedef int32_t vertex_t;
typedef int32_t edge_t;
typedef float weight_t;

const weight_t EPSILON = 0.001;
const float EPSILON = 0.001;

int generic_sssp_test(const cugraph_resource_handle_t* p_handle,
vertex_t* h_src,
vertex_t* h_dst,
weight_t* h_wgt,
float* h_wgt,
vertex_t source,
weight_t const* expected_distances,
float const* expected_distances,
vertex_t const* expected_predecessors,
size_t num_vertices,
size_t num_edges,
weight_t cutoff,
float cutoff,
bool_t store_transposed)
{
int test_ret_value = 0;
Expand All @@ -64,7 +63,7 @@ int generic_sssp_test(const cugraph_resource_handle_t* p_handle,
predecessors = cugraph_paths_result_get_predecessors(p_result);

vertex_t h_vertices[num_vertices];
weight_t h_distances[num_vertices];
float h_distances[num_vertices];
vertex_t h_predecessors[num_vertices];

ret_code = cugraph_type_erased_device_array_view_copy_to_host(
Expand Down Expand Up @@ -101,15 +100,88 @@ int generic_sssp_test(const cugraph_resource_handle_t* p_handle,
return test_ret_value;
}

int generic_sssp_test_double(const cugraph_resource_handle_t* p_handle,
vertex_t* h_src,
vertex_t* h_dst,
double* h_wgt,
vertex_t source,
double const* expected_distances,
vertex_t const* expected_predecessors,
size_t num_vertices,
size_t num_edges,
double cutoff,
bool_t store_transposed)
{
int test_ret_value = 0;

cugraph_error_code_t ret_code = CUGRAPH_SUCCESS;
cugraph_error_t* ret_error;

cugraph_graph_t* p_graph = NULL;
cugraph_paths_result_t* p_result = NULL;

ret_code = create_mg_test_graph_double(
p_handle, h_src, h_dst, h_wgt, num_edges, store_transposed, FALSE, &p_graph, &ret_error);

ret_code = cugraph_sssp(p_handle, p_graph, source, cutoff, TRUE, FALSE, &p_result, &ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error));
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_sssp failed.");

cugraph_type_erased_device_array_view_t* vertices;
cugraph_type_erased_device_array_view_t* distances;
cugraph_type_erased_device_array_view_t* predecessors;

vertices = cugraph_paths_result_get_vertices(p_result);
distances = cugraph_paths_result_get_distances(p_result);
predecessors = cugraph_paths_result_get_predecessors(p_result);

vertex_t h_vertices[num_vertices];
double h_distances[num_vertices];
vertex_t h_predecessors[num_vertices];

ret_code = cugraph_type_erased_device_array_view_copy_to_host(
p_handle, (byte_t*)h_vertices, vertices, &ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed.");

ret_code = cugraph_type_erased_device_array_view_copy_to_host(
p_handle, (byte_t*)h_distances, distances, &ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed.");

ret_code = cugraph_type_erased_device_array_view_copy_to_host(
p_handle, (byte_t*)h_predecessors, predecessors, &ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed.");

size_t num_local_vertices = cugraph_type_erased_device_array_view_size(vertices);

for (int i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) {
TEST_ASSERT(test_ret_value,
nearlyEqualDouble(expected_distances[h_vertices[i]], h_distances[i], EPSILON),
"sssp distances don't match");

TEST_ASSERT(test_ret_value,
expected_predecessors[h_vertices[i]] == h_predecessors[i],
"sssp predecessors don't match");
}

cugraph_type_erased_device_array_view_free(vertices);
cugraph_type_erased_device_array_view_free(distances);
cugraph_type_erased_device_array_view_free(predecessors);
cugraph_paths_result_free(p_result);
cugraph_sg_graph_free(p_graph);
cugraph_error_free(ret_error);

return test_ret_value;
}

int test_sssp(const cugraph_resource_handle_t* p_handle)
{
size_t num_edges = 8;
size_t num_vertices = 6;

vertex_t src[] = {0, 1, 1, 2, 2, 2, 3, 4};
vertex_t dst[] = {1, 3, 4, 0, 1, 3, 5, 5};
weight_t wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f};
weight_t expected_distances[] = {0.0f, 0.1f, FLT_MAX, 2.2f, 1.2f, 4.4f};
float wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f};
float expected_distances[] = {0.0f, 0.1f, FLT_MAX, 2.2f, 1.2f, 4.4f};
vertex_t expected_predecessors[] = {-1, 0, -1, 1, 1, 4};

// Bfs wants store_transposed = FALSE
Expand All @@ -133,8 +205,8 @@ int test_sssp_with_transpose(const cugraph_resource_handle_t* p_handle)

vertex_t src[] = {0, 1, 1, 2, 2, 2, 3, 4};
vertex_t dst[] = {1, 3, 4, 0, 1, 3, 5, 5};
weight_t wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f};
weight_t expected_distances[] = {0.0f, 0.1f, FLT_MAX, 2.2f, 1.2f, 4.4f};
float wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f};
float expected_distances[] = {0.0f, 0.1f, FLT_MAX, 2.2f, 1.2f, 4.4f};
vertex_t expected_predecessors[] = {-1, 0, -1, 1, 1, 4};

// Bfs wants store_transposed = FALSE
Expand All @@ -152,6 +224,32 @@ int test_sssp_with_transpose(const cugraph_resource_handle_t* p_handle)
TRUE);
}

int test_sssp_with_transpose_double(const cugraph_resource_handle_t* p_handle)
{
size_t num_edges = 8;
size_t num_vertices = 6;

vertex_t src[] = {0, 1, 1, 2, 2, 2, 3, 4};
vertex_t dst[] = {1, 3, 4, 0, 1, 3, 5, 5};
double wgt[] = {0.1d, 2.1d, 1.1d, 5.1d, 3.1d, 4.1d, 7.2d, 3.2d};
double expected_distances[] = {0.0d, 0.1d, DBL_MAX, 2.2d, 1.2d, 4.4d};
vertex_t expected_predecessors[] = {-1, 0, -1, 1, 1, 4};

// Bfs wants store_transposed = FALSE
// This call will force cugraph_sssp to transpose the graph
return generic_sssp_test_double(p_handle,
src,
dst,
wgt,
0,
expected_distances,
expected_predecessors,
num_vertices,
num_edges,
10,
TRUE);
}

/******************************************************************************/

int main(int argc, char** argv)
Expand All @@ -178,6 +276,7 @@ int main(int argc, char** argv)
int result = 0;
result |= RUN_MG_TEST(test_sssp, handle);
result |= RUN_MG_TEST(test_sssp_with_transpose, handle);
result |= RUN_MG_TEST(test_sssp_with_transpose_double, handle);

cugraph_free_resource_handle(handle);
free_raft_handle(raft_handle);
Expand Down
101 changes: 101 additions & 0 deletions cpp/tests/c_api/mg_test_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,107 @@ extern "C" int create_mg_test_graph(const cugraph_resource_handle_t* handle,

return test_ret_value;
}

/*
* Simple function to create an MG test graph from COO arrays. COO is
* assumed to be defined entirely on rank 0 and will be shuffled to
* the proper location.
*/
extern "C" int create_mg_test_graph_double(const cugraph_resource_handle_t* handle,
int32_t* h_src,
int32_t* h_dst,
double* h_wgt,
size_t num_edges,
bool_t store_transposed,
bool_t is_symmetric,
cugraph_graph_t** p_graph,
cugraph_error_t** ret_error)
{
int test_ret_value = 0;
cugraph_error_code_t ret_code;
cugraph_graph_properties_t properties;

properties.is_symmetric = is_symmetric;
properties.is_multigraph = FALSE;

data_type_id_t vertex_tid = INT32;
data_type_id_t edge_tid = INT32;
data_type_id_t weight_tid = FLOAT64;

cugraph_type_erased_device_array_t* src;
cugraph_type_erased_device_array_t* dst;
cugraph_type_erased_device_array_t* wgt;
cugraph_type_erased_device_array_view_t* src_view;
cugraph_type_erased_device_array_view_t* dst_view;
cugraph_type_erased_device_array_view_t* wgt_view;

int rank = 0;

rank = cugraph_resource_handle_get_rank(handle);

if (rank == 0) {
ret_code =
cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed.");
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error));

ret_code =
cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed.");

ret_code =
cugraph_type_erased_device_array_create(handle, num_edges, weight_tid, &wgt, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed.");
} else {
ret_code = cugraph_type_erased_device_array_create(handle, 0, vertex_tid, &src, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed.");
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error));

ret_code = cugraph_type_erased_device_array_create(handle, 0, vertex_tid, &dst, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed.");

ret_code = cugraph_type_erased_device_array_create(handle, 0, weight_tid, &wgt, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed.");
}

src_view = cugraph_type_erased_device_array_view(src);
dst_view = cugraph_type_erased_device_array_view(dst);
wgt_view = cugraph_type_erased_device_array_view(wgt);

ret_code = cugraph_type_erased_device_array_view_copy_from_host(
handle, src_view, (byte_t*)h_src, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src copy_from_host failed.");

ret_code = cugraph_type_erased_device_array_view_copy_from_host(
handle, dst_view, (byte_t*)h_dst, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst copy_from_host failed.");

ret_code = cugraph_type_erased_device_array_view_copy_from_host(
handle, wgt_view, (byte_t*)h_wgt, ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed.");

ret_code = cugraph_mg_graph_create(handle,
&properties,
src_view,
dst_view,
wgt_view,
store_transposed,
num_edges,
FALSE,
p_graph,
ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed.");

cugraph_type_erased_device_array_view_free(wgt_view);
cugraph_type_erased_device_array_view_free(dst_view);
cugraph_type_erased_device_array_view_free(src_view);
cugraph_type_erased_device_array_free(wgt);
cugraph_type_erased_device_array_free(dst);
cugraph_type_erased_device_array_free(src);

return test_ret_value;
}

extern "C" int create_mg_test_graph_with_edge_ids(const cugraph_resource_handle_t* handle,
int32_t* h_src,
int32_t* h_dst,
Expand Down
10 changes: 10 additions & 0 deletions cpp/tests/c_api/mg_test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ int create_mg_test_graph(const cugraph_resource_handle_t* p_handle,
cugraph_graph_t** p_graph,
cugraph_error_t** ret_error);

int create_mg_test_graph_double(const cugraph_resource_handle_t* p_handle,
int32_t* h_src,
int32_t* h_dst,
double* h_wgt,
size_t num_edges,
bool_t store_transposed,
bool_t is_symmetric,
cugraph_graph_t** p_graph,
cugraph_error_t** ret_error);

int create_mg_test_graph_with_edge_ids(const cugraph_resource_handle_t* p_handle,
int32_t* h_src,
int32_t* h_dst,
Expand Down
Loading

0 comments on commit b0c6a9e

Please sign in to comment.