From b0c6a9ec54b1f1c659b9e83a21f2ff5f99a8f0fe Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Thu, 26 May 2022 12:16:41 -0400 Subject: [PATCH] =?UTF-8?q?Add=20test=20to=20reproduce=20issue=20with=20do?= =?UTF-8?q?uble=20weights,=20fix=20issue=20(graph=20cre=E2=80=A6=20(#2305)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @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: https://github.com/rapidsai/cugraph/pull/2305 --- cpp/include/cugraph_c/graph.h | 9 +- cpp/src/c_api/graph_mg.cpp | 4 +- cpp/src/c_api/graph_sg.cpp | 4 +- cpp/tests/c_api/c_test_utils.h | 13 +++ cpp/tests/c_api/mg_sssp_test.c | 119 +++++++++++++++++++++++--- cpp/tests/c_api/mg_test_utils.cpp | 101 ++++++++++++++++++++++ cpp/tests/c_api/mg_test_utils.h | 10 +++ cpp/tests/c_api/sssp_test.c | 134 ++++++++++++++++++++++++------ cpp/tests/c_api/test_utils.cpp | 88 ++++++++++++++++++++ 9 files changed, 440 insertions(+), 42 deletions(-) diff --git a/cpp/include/cugraph_c/graph.h b/cpp/include/cugraph_c/graph.h index 7dd880a6919..dfd27ef27d0 100644 --- a/cpp/include/cugraph_c/graph.h +++ b/cpp/include/cugraph_c/graph.h @@ -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 @@ -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 diff --git a/cpp/src/c_api/graph_mg.cpp b/cpp/src/c_api/graph_mg.cpp index dc1253b5a13..7c77050a5ab 100644 --- a/cpp/src/c_api/graph_mg.cpp +++ b/cpp/src/c_api/graph_mg.cpp @@ -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); @@ -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; diff --git a/cpp/src/c_api/graph_sg.cpp b/cpp/src/c_api/graph_sg.cpp index 76239680a69..ea4ce2389ab 100644 --- a/cpp/src/c_api/graph_sg.cpp +++ b/cpp/src/c_api/graph_sg.cpp @@ -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); @@ -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; diff --git a/cpp/tests/c_api/c_test_utils.h b/cpp/tests/c_api/c_test_utils.h index 71eea9f0ff2..cf9b1deed9c 100644 --- a/cpp/tests/c_api/c_test_utils.h +++ b/cpp/tests/c_api/c_test_utils.h @@ -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, @@ -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); diff --git a/cpp/tests/c_api/mg_sssp_test.c b/cpp/tests/c_api/mg_sssp_test.c index 3aca77dca28..6392addc397 100644 --- a/cpp/tests/c_api/mg_sssp_test.c +++ b/cpp/tests/c_api/mg_sssp_test.c @@ -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; @@ -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( @@ -101,6 +100,79 @@ 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; @@ -108,8 +180,8 @@ int test_sssp(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 @@ -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 @@ -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) @@ -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); diff --git a/cpp/tests/c_api/mg_test_utils.cpp b/cpp/tests/c_api/mg_test_utils.cpp index 53ce87abe23..98bea41f1b7 100644 --- a/cpp/tests/c_api/mg_test_utils.cpp +++ b/cpp/tests/c_api/mg_test_utils.cpp @@ -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, diff --git a/cpp/tests/c_api/mg_test_utils.h b/cpp/tests/c_api/mg_test_utils.h index 2aecbc13cc6..135d4195f57 100644 --- a/cpp/tests/c_api/mg_test_utils.h +++ b/cpp/tests/c_api/mg_test_utils.h @@ -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, diff --git a/cpp/tests/c_api/sssp_test.c b/cpp/tests/c_api/sssp_test.c index dc9f7a23f47..1113ed88e14 100644 --- a/cpp/tests/c_api/sssp_test.c +++ b/cpp/tests/c_api/sssp_test.c @@ -19,24 +19,23 @@ #include #include -#include #include +#include 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(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; @@ -44,9 +43,9 @@ int generic_sssp_test(vertex_t* h_src, cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; cugraph_error_t* ret_error; - cugraph_resource_handle_t* p_handle = NULL; - cugraph_graph_t* p_graph = NULL; - cugraph_paths_result_t* p_result = NULL; + cugraph_resource_handle_t* p_handle = NULL; + cugraph_graph_t* p_graph = NULL; + cugraph_paths_result_t* p_result = NULL; p_handle = cugraph_create_resource_handle(NULL); TEST_ASSERT(test_ret_value, p_handle != NULL, "resource handle creation failed."); @@ -54,8 +53,7 @@ int generic_sssp_test(vertex_t* h_src, ret_code = create_test_graph( p_handle, h_src, h_dst, h_wgt, num_edges, store_transposed, FALSE, FALSE, &p_graph, &ret_error); - ret_code = cugraph_sssp( - p_handle, p_graph, source, cutoff, TRUE, FALSE, &p_result, &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_sssp failed."); cugraph_type_erased_device_array_view_t* vertices; @@ -67,7 +65,7 @@ int generic_sssp_test(vertex_t* h_src, 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( @@ -103,6 +101,80 @@ int generic_sssp_test(vertex_t* h_src, return test_ret_value; } +int generic_sssp_test_double(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_resource_handle_t* p_handle = NULL; + cugraph_graph_t* p_graph = NULL; + cugraph_paths_result_t* p_result = NULL; + + p_handle = cugraph_create_resource_handle(NULL); + TEST_ASSERT(test_ret_value, p_handle != NULL, "resource handle creation failed."); + + ret_code = create_test_graph_double( + p_handle, h_src, h_dst, h_wgt, num_edges, store_transposed, FALSE, 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_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."); + + for (int i = 0; (i < num_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_free_resource_handle(p_handle); + cugraph_error_free(ret_error); + + return test_ret_value; +} + int test_sssp() { size_t num_edges = 8; @@ -110,8 +182,8 @@ int test_sssp() 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 @@ -134,22 +206,31 @@ int test_sssp_with_transpose() 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 // This call will force cugraph_sssp to transpose the graph - return generic_sssp_test(src, - dst, - wgt, - 0, - expected_distances, - expected_predecessors, - num_vertices, - num_edges, - 10, - TRUE); + return generic_sssp_test( + src, dst, wgt, 0, expected_distances, expected_predecessors, num_vertices, num_edges, 10, TRUE); +} + +int test_sssp_with_transpose_double() +{ + 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( + src, dst, wgt, 0, expected_distances, expected_predecessors, num_vertices, num_edges, 10, TRUE); } /******************************************************************************/ @@ -159,5 +240,6 @@ int main(int argc, char** argv) int result = 0; result |= RUN_TEST(test_sssp); result |= RUN_TEST(test_sssp_with_transpose); + result |= RUN_TEST(test_sssp_with_transpose_double); return result; } diff --git a/cpp/tests/c_api/test_utils.cpp b/cpp/tests/c_api/test_utils.cpp index 539e6869ec5..064a9469243 100644 --- a/cpp/tests/c_api/test_utils.cpp +++ b/cpp/tests/c_api/test_utils.cpp @@ -26,6 +26,14 @@ extern "C" int nearlyEqual(float a, float b, float epsilon) return (fabsf(a - b) <= (((fabsf(a) < fabsf(b)) ? fabs(b) : fabs(a)) * epsilon)); } +extern "C" int nearlyEqualDouble(double a, double b, double epsilon) +{ + // FIXME: There is a better test than this, + // perhaps use the gtest comparison for consistency + // with C++ and wrap it in a C wrapper. + return (fabsf(a - b) <= (((fabsf(a) < fabsf(b)) ? fabs(b) : fabs(a)) * epsilon)); +} + /* * Simple check of creating a graph from a COO on device memory. */ @@ -109,6 +117,86 @@ extern "C" int create_test_graph(const cugraph_resource_handle_t* p_handle, return test_ret_value; } +extern "C" 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) +{ + 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; + + ret_code = + cugraph_type_erased_device_array_create(p_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(p_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(p_handle, num_edges, 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( + p_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( + p_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( + p_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_sg_graph_create(p_handle, + &properties, + src_view, + dst_view, + wgt_view, + store_transposed, + renumber, + 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; +} + /* * Runs the function pointed to by "test" and returns the return code. Also * prints reporting info (using "test_name"): pass/fail and run time, to stdout.