chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:35:51 +08:00
commit c36a561cd8
2172 changed files with 455595 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
#ifndef TEST_COMMON_H_
#define TEST_COMMON_H_
#include <dgl/runtime/ndarray.h>
static constexpr DGLContext CTX = DGLContext{kDGLCPU, 0};
static constexpr DGLContext CPU = DGLContext{kDGLCPU, 0};
#ifdef DGL_USE_CUDA
static constexpr DGLContext GPU = DGLContext{kDGLCUDA, 0};
#endif
template <typename T>
inline T* Ptr(dgl::runtime::NDArray nd) {
return static_cast<T*>(nd->data);
}
inline int64_t* PI64(dgl::runtime::NDArray nd) {
return static_cast<int64_t*>(nd->data);
}
inline int32_t* PI32(dgl::runtime::NDArray nd) {
return static_cast<int32_t*>(nd->data);
}
inline int64_t Len(dgl::runtime::NDArray nd) { return nd->shape[0]; }
template <typename T>
inline bool ArrayEQ(dgl::runtime::NDArray a1, dgl::runtime::NDArray a2) {
if (a1->ndim != a2->ndim) return false;
if (a1->dtype != a2->dtype) return false;
if (a1->ctx != a2->ctx) return false;
if (a1.NumElements() != a2.NumElements()) return false;
if (a1.NumElements() == 0) return true;
int64_t num = 1;
for (int i = 0; i < a1->ndim; ++i) {
if (a1->shape[i] != a2->shape[i]) return false;
num *= a1->shape[i];
}
a1 = a1.CopyTo(CPU);
a2 = a2.CopyTo(CPU);
for (int64_t i = 0; i < num; ++i)
if (static_cast<T*>(a1->data)[i] != static_cast<T*>(a2->data)[i])
return false;
return true;
}
template <typename T>
inline bool IsInArray(dgl::runtime::NDArray a, T x) {
if (!a.defined() || a->shape[0] == 0) return false;
for (int64_t i = 0; i < a->shape[0]; ++i) {
if (x == static_cast<T*>(a->data)[i]) return true;
}
return false;
}
#endif // TEST_COMMON_H_
+13
View File
@@ -0,0 +1,13 @@
/**
* Copyright (c) 2019 by Contributors
* @file graph_index_test.cc
* @brief Test GraphIndex
*/
#include <dgl/graph.h>
#include <gtest/gtest.h>
TEST(GraphTest, TestNumVertices) {
dgl::Graph g;
g.AddVertices(10);
ASSERT_EQ(g.NumVertices(), 10);
};
+105
View File
@@ -0,0 +1,105 @@
/**
* Copyright (c) 2019 by Contributors
* @file msg_queue.cc
* @brief Message queue for DGL distributed training.
*/
#include <gtest/gtest.h>
#include <string>
#include <thread>
#include <vector>
#include "../src/rpc/network/msg_queue.h"
using dgl::network::Message;
using dgl::network::MessageQueue;
using std::string;
TEST(MessageQueueTest, AddRemove) {
MessageQueue queue(5, 1); // size:5, num_of_producer:1
// msg 1
std::string str_1("111");
Message msg_1 = {const_cast<char*>(str_1.data()), 3};
EXPECT_EQ(queue.Add(msg_1), ADD_SUCCESS);
// msg 2
std::string str_2("22");
Message msg_2 = {const_cast<char*>(str_2.data()), 2};
EXPECT_EQ(queue.Add(msg_2), ADD_SUCCESS);
// msg 3
std::string str_3("xxxx");
Message msg_3 = {const_cast<char*>(str_3.data()), 4};
EXPECT_EQ(queue.Add(msg_3, false), QUEUE_FULL);
// msg 4
Message msg_4;
EXPECT_EQ(queue.Remove(&msg_4), REMOVE_SUCCESS);
EXPECT_EQ(string(msg_4.data, msg_4.size), string("111"));
// msg 5
Message msg_5;
EXPECT_EQ(queue.Remove(&msg_5), REMOVE_SUCCESS);
EXPECT_EQ(string(msg_5.data, msg_5.size), string("22"));
// msg 6
std::string str_6("33333");
Message msg_6 = {const_cast<char*>(str_6.data()), 5};
EXPECT_EQ(queue.Add(msg_6), ADD_SUCCESS);
// msg 7
Message msg_7;
EXPECT_EQ(queue.Remove(&msg_7), REMOVE_SUCCESS);
EXPECT_EQ(string(msg_7.data, msg_7.size), string("33333"));
// msg 8
Message msg_8;
EXPECT_EQ(queue.Remove(&msg_8, false), QUEUE_EMPTY); // non-blocking remove
// msg 9
std::string str_9("666666");
Message msg_9 = {const_cast<char*>(str_9.data()), 6};
EXPECT_EQ(queue.Add(msg_9), MSG_GT_SIZE); // exceed queue size
// msg 10
std::string str_10("55555");
Message msg_10 = {const_cast<char*>(str_10.data()), 5};
EXPECT_EQ(queue.Add(msg_10), ADD_SUCCESS);
// msg 11
Message msg_11;
EXPECT_EQ(queue.Remove(&msg_11), REMOVE_SUCCESS);
}
TEST(MessageQueueTest, EmptyAndNoMoreAdd) {
MessageQueue queue(5, 2); // size:5, num_of_producer:2
EXPECT_EQ(queue.EmptyAndNoMoreAdd(), false);
EXPECT_EQ(queue.Empty(), true);
queue.SignalFinished(1);
queue.SignalFinished(1);
EXPECT_EQ(queue.EmptyAndNoMoreAdd(), false);
queue.SignalFinished(2);
EXPECT_EQ(queue.EmptyAndNoMoreAdd(), true);
}
const int kNumOfProducer = 100;
const int kNumOfMessage = 100;
std::string str_apple("apple");
void start_add(MessageQueue* queue, int id) {
for (int i = 0; i < kNumOfMessage; ++i) {
Message msg = {const_cast<char*>(str_apple.data()), 5};
EXPECT_EQ(queue->Add(msg), ADD_SUCCESS);
}
queue->SignalFinished(id);
}
TEST(MessageQueueTest, MultiThread) {
MessageQueue queue(100000, kNumOfProducer);
EXPECT_EQ(queue.EmptyAndNoMoreAdd(), false);
EXPECT_EQ(queue.Empty(), true);
std::vector<std::thread> thread_pool(kNumOfProducer);
for (int i = 0; i < kNumOfProducer; ++i) {
thread_pool[i] = std::thread(start_add, &queue, i);
}
for (int i = 0; i < kNumOfProducer * kNumOfMessage; ++i) {
Message msg;
EXPECT_EQ(queue.Remove(&msg), REMOVE_SUCCESS);
EXPECT_EQ(string(msg.data, msg.size), string("apple"));
}
for (int i = 0; i < kNumOfProducer; ++i) {
thread_pool[i].join();
}
EXPECT_EQ(queue.EmptyAndNoMoreAdd(), true);
}
+246
View File
@@ -0,0 +1,246 @@
/**
* Copyright (c) 2019 by Contributors
* @file socket_communicator_test.cc
* @brief Test SocketCommunicator
*/
#include "../src/rpc/network/socket_communicator.h"
#include <gtest/gtest.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <chrono>
#include <fstream>
#include <streambuf>
#include <string>
#include <thread>
#include <vector>
#include "../src/rpc/network/msg_queue.h"
using std::string;
using dgl::network::DefaultMessageDeleter;
using dgl::network::Message;
using dgl::network::SocketReceiver;
using dgl::network::SocketSender;
const int64_t kQueueSize = 500 * 1024;
const int kThreadNum = 2;
const int kMaxTryTimes = 1024;
#ifndef WIN32
const int kNumSender = 3;
const int kNumReceiver = 3;
const int kNumMessage = 10;
const char* ip_addr[] = {
"tcp://127.0.0.1:50091", "tcp://127.0.0.1:50092", "tcp://127.0.0.1:50093"};
static void start_client();
static void start_server(int id);
TEST(SocketCommunicatorTest, SendAndRecv) {
// start 10 client
std::vector<std::thread> client_thread(kNumSender);
for (int i = 0; i < kNumSender; ++i) {
client_thread[i] = std::thread(start_client);
}
// start 10 server
std::vector<std::thread> server_thread(kNumReceiver);
for (int i = 0; i < kNumReceiver; ++i) {
server_thread[i] = std::thread(start_server, i);
}
for (int i = 0; i < kNumSender; ++i) {
client_thread[i].join();
}
for (int i = 0; i < kNumReceiver; ++i) {
server_thread[i].join();
}
}
TEST(SocketCommunicatorTest, SendAndRecvTimeout) {
std::atomic_bool stop{false};
// start 1 client, connect to 1 server, send 2 messsage
auto client = std::thread([&stop]() {
SocketSender sender(kQueueSize, kThreadNum);
sender.ConnectReceiver(ip_addr[0], 0);
sender.ConnectReceiverFinalize(kMaxTryTimes);
for (int i = 0; i < 2; ++i) {
char* str_data = new char[9];
memcpy(str_data, "123456789", 9);
Message msg = {str_data, 9};
msg.deallocator = DefaultMessageDeleter;
EXPECT_EQ(sender.Send(msg, 0), ADD_SUCCESS);
}
while (!stop) {
}
sender.Finalize();
});
// start 1 server, accept 1 client, receive 2 message
auto server = std::thread([&stop]() {
SocketReceiver receiver(kQueueSize, kThreadNum);
receiver.Wait(ip_addr[0], 1);
Message msg;
int recv_id;
// receive 1st message
EXPECT_EQ(receiver.RecvFrom(&msg, 0, 0), REMOVE_SUCCESS);
EXPECT_EQ(string(msg.data, msg.size), string("123456789"));
msg.deallocator(&msg);
// receive 2nd message
EXPECT_EQ(receiver.Recv(&msg, &recv_id, 0), REMOVE_SUCCESS);
EXPECT_EQ(string(msg.data, msg.size), string("123456789"));
msg.deallocator(&msg);
// timed out
EXPECT_EQ(receiver.RecvFrom(&msg, 0, 1000), QUEUE_EMPTY);
EXPECT_EQ(receiver.Recv(&msg, &recv_id, 1000), QUEUE_EMPTY);
stop = true;
receiver.Finalize();
});
// join
client.join();
server.join();
}
void start_client() {
SocketSender sender(kQueueSize, kThreadNum);
for (int i = 0; i < kNumReceiver; ++i) {
sender.ConnectReceiver(ip_addr[i], i);
}
sender.ConnectReceiverFinalize(kMaxTryTimes);
for (int i = 0; i < kNumMessage; ++i) {
for (int n = 0; n < kNumReceiver; ++n) {
char* str_data = new char[9];
memcpy(str_data, "123456789", 9);
Message msg = {str_data, 9};
msg.deallocator = DefaultMessageDeleter;
EXPECT_EQ(sender.Send(msg, n), ADD_SUCCESS);
}
}
for (int i = 0; i < kNumMessage; ++i) {
for (int n = 0; n < kNumReceiver; ++n) {
char* str_data = new char[9];
memcpy(str_data, "123456789", 9);
Message msg = {str_data, 9};
msg.deallocator = DefaultMessageDeleter;
EXPECT_EQ(sender.Send(msg, n), ADD_SUCCESS);
}
}
sender.Finalize();
}
void start_server(int id) {
sleep(5);
SocketReceiver receiver(kQueueSize, kThreadNum);
receiver.Wait(ip_addr[id], kNumSender);
for (int i = 0; i < kNumMessage; ++i) {
for (int n = 0; n < kNumSender; ++n) {
Message msg;
EXPECT_EQ(receiver.RecvFrom(&msg, n), REMOVE_SUCCESS);
EXPECT_EQ(string(msg.data, msg.size), string("123456789"));
msg.deallocator(&msg);
}
}
for (int n = 0; n < kNumSender * kNumMessage; ++n) {
Message msg;
int recv_id;
EXPECT_EQ(receiver.Recv(&msg, &recv_id), REMOVE_SUCCESS);
EXPECT_EQ(string(msg.data, msg.size), string("123456789"));
msg.deallocator(&msg);
}
receiver.Finalize();
}
TEST(SocketCommunicatorTest, TCPSocketBind) {
dgl::network::TCPSocket socket;
testing::internal::CaptureStderr();
EXPECT_EQ(socket.Bind("127.0.0", 50001), false);
const std::string stderr = testing::internal::GetCapturedStderr();
EXPECT_NE(stderr.find("Invalid IP: 127.0.0"), std::string::npos);
}
#else
#include <windows.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
void sleep(int seconds) { Sleep(seconds * 1000); }
static void start_client();
static bool start_server();
DWORD WINAPI _ClientThreadFunc(LPVOID param) {
start_client();
return 0;
}
DWORD WINAPI _ServerThreadFunc(LPVOID param) { return start_server() ? 1 : 0; }
TEST(SocketCommunicatorTest, SendAndRecv) {
HANDLE hThreads[2];
WSADATA wsaData;
DWORD retcode, exitcode;
srand((unsigned)time(NULL));
int port = (rand() % (5000 - 3000 + 1)) + 3000;
std::string ip_addr = "tcp://127.0.0.1:" + std::to_string(port);
std::ofstream out("addr.txt");
out << ip_addr;
out.close();
ASSERT_EQ(::WSAStartup(MAKEWORD(2, 2), &wsaData), 0);
hThreads[0] =
::CreateThread(NULL, 0, _ClientThreadFunc, NULL, 0, NULL); // client
ASSERT_TRUE(hThreads[0] != NULL);
hThreads[1] =
::CreateThread(NULL, 0, _ServerThreadFunc, NULL, 0, NULL); // server
ASSERT_TRUE(hThreads[1] != NULL);
retcode = ::WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);
EXPECT_TRUE((retcode <= WAIT_OBJECT_0 + 1) && (retcode >= WAIT_OBJECT_0));
EXPECT_EQ(::GetExitCodeThread(hThreads[1], &exitcode), TRUE);
EXPECT_EQ(exitcode, 1);
EXPECT_EQ(::CloseHandle(hThreads[0]), TRUE);
EXPECT_EQ(::CloseHandle(hThreads[1]), TRUE);
::WSACleanup();
}
static void start_client() {
std::ifstream t("addr.txt");
std::string ip_addr(
(std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
t.close();
SocketSender sender(kQueueSize, kThreadNum);
sender.ConnectReceiver(ip_addr.c_str(), 0);
sender.ConnectReceiverFinalize(kMaxTryTimes);
char* str_data = new char[9];
memcpy(str_data, "123456789", 9);
Message msg = {str_data, 9};
msg.deallocator = DefaultMessageDeleter;
sender.Send(msg, 0);
sender.Finalize();
}
static bool start_server() {
sleep(5);
std::ifstream t("addr.txt");
std::string ip_addr(
(std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
t.close();
SocketReceiver receiver(kQueueSize, kThreadNum);
receiver.Wait(ip_addr.c_str(), 1);
Message msg;
EXPECT_EQ(receiver.RecvFrom(&msg, 0), REMOVE_SUCCESS);
receiver.Finalize();
return string("123456789") == string(msg.data, msg.size);
}
#endif
+52
View File
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2019 by Contributors
* @file string_test.cc
* @brief Test String Common
*/
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include "../src/rpc/network/common.h"
using dgl::network::SplitStringUsing;
using dgl::network::SStringPrintf;
using dgl::network::StringAppendF;
using dgl::network::StringPrintf;
TEST(SplitStringTest, SplitStringUsingCompoundDelim) {
std::string full(" apple \torange ");
std::vector<std::string> subs;
SplitStringUsing(full, " \t", &subs);
EXPECT_EQ(subs.size(), 2);
EXPECT_EQ(subs[0], std::string("apple"));
EXPECT_EQ(subs[1], std::string("orange"));
}
TEST(SplitStringTest, testSplitStringUsingSingleDelim) {
std::string full(" apple orange ");
std::vector<std::string> subs;
SplitStringUsing(full, " ", &subs);
EXPECT_EQ(subs.size(), 2);
EXPECT_EQ(subs[0], std::string("apple"));
EXPECT_EQ(subs[1], std::string("orange"));
}
TEST(SplitStringTest, testSplitingNoDelimString) {
std::string full("apple");
std::vector<std::string> subs;
SplitStringUsing(full, " ", &subs);
EXPECT_EQ(subs.size(), 1);
EXPECT_EQ(subs[0], std::string("apple"));
}
TEST(StringPrintf, normal) {
using std::string;
EXPECT_EQ(StringPrintf("%d", 1), string("1"));
string target;
SStringPrintf(&target, "%d", 1);
EXPECT_EQ(target, string("1"));
StringAppendF(&target, "%d", 2);
EXPECT_EQ(target, string("12"));
}
File diff suppressed because it is too large Load Diff
+75
View File
@@ -0,0 +1,75 @@
#include <dgl/array.h>
#include <dgl/runtime/parallel_for.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <set>
#include "../../src/array/cpu/concurrent_id_hash_map.h"
#include "./common.h"
using namespace dgl;
using namespace dgl::runtime;
using namespace dgl::aten;
namespace {
template <typename IdType>
size_t ConstructRandomSet(
size_t size, IdType range, std::vector<IdType>& id_vec) {
id_vec.resize(size);
std::srand(std::time(nullptr));
for (size_t i = 0; i < size; i++) {
id_vec[i] = static_cast<IdType>(std::rand() % range);
}
size_t num_seeds = size / 5 + 1;
std::sort(id_vec.begin(), id_vec.begin() + num_seeds);
return std::unique(id_vec.begin(), id_vec.begin() + num_seeds) -
id_vec.begin();
}
template <typename IdType, size_t size, IdType range>
void _TestIdMap() {
std::vector<IdType> id_vec;
auto num_seeds = ConstructRandomSet(size, range, id_vec);
std::set<IdType> id_set(id_vec.begin(), id_vec.end());
IdArray ids = VecToIdArray(id_vec, sizeof(IdType) * 8, CTX);
ConcurrentIdHashMap<IdType> id_map;
IdArray unique_ids = id_map.Init(ids, num_seeds);
auto unique_num = static_cast<size_t>(unique_ids->shape[0]);
IdType* unique_id_data = unique_ids.Ptr<IdType>();
EXPECT_EQ(id_set.size(), unique_num);
parallel_for(0, num_seeds, 64, [&](int64_t s, int64_t e) {
for (int64_t i = s; i < e; i++) {
EXPECT_EQ(id_vec[i], unique_id_data[i]);
}
});
parallel_for(num_seeds, unique_num, 128, [&](int64_t s, int64_t e) {
for (int64_t i = s; i < e; i++) {
EXPECT_TRUE(id_set.find(unique_id_data[i]) != id_set.end());
}
});
IdArray new_ids = id_map.MapIds(unique_ids);
EXPECT_TRUE(new_ids.IsContiguous());
ids->shape[0] = num_seeds;
IdArray new_seed_ids = id_map.MapIds(ids);
EXPECT_TRUE(new_seed_ids.IsContiguous());
EXPECT_EQ(new_seed_ids.Ptr<IdType>()[0], static_cast<IdType>(0));
}
TEST(ConcurrentIdHashMapTest, TestConcurrentIdHashMap) {
_TestIdMap<int32_t, 1, 10>();
_TestIdMap<int64_t, 1, 10>();
_TestIdMap<int32_t, 1000, 500000>();
_TestIdMap<int64_t, 1000, 500000>();
_TestIdMap<int32_t, 50000, 1000000>();
_TestIdMap<int64_t, 50000, 1000000>();
_TestIdMap<int32_t, 100000, 40000000>();
_TestIdMap<int64_t, 100000, 40000000>();
}
}; // namespace
+215
View File
@@ -0,0 +1,215 @@
#include <dgl/array.h>
#include <dgl/kernel.h>
#include <gtest/gtest.h>
#include "../../src/array/cpu/array_utils.h" // PairHash
#include "./common.h"
using namespace dgl;
using namespace dgl::runtime;
namespace {
// Unit tests:
// CSRMM(A, B) == A_mm_B
// CSRSum({A, C}) == A_plus_C
// CSRMask(A, C) = A_mask_C
template <typename IdType, typename DType>
std::unordered_map<std::pair<IdType, IdType>, DType, aten::PairHash> COOToMap(
aten::COOMatrix coo, NDArray weights) {
std::unordered_map<std::pair<IdType, IdType>, DType, aten::PairHash> map;
for (int64_t i = 0; i < coo.row->shape[0]; ++i) {
IdType irow = aten::IndexSelect<IdType>(coo.row, i);
IdType icol = aten::IndexSelect<IdType>(coo.col, i);
IdType ieid =
aten::COOHasData(coo) ? aten::IndexSelect<IdType>(coo.data, i) : i;
DType idata = aten::IndexSelect<DType>(weights, ieid);
map.insert({{irow, icol}, idata});
}
return map;
}
template <typename IdType, typename DType>
bool CSRIsClose(
aten::CSRMatrix A, aten::CSRMatrix B, NDArray A_weights, NDArray B_weights,
DType rtol, DType atol) {
auto Amap = COOToMap<IdType, DType>(CSRToCOO(A, false), A_weights);
auto Bmap = COOToMap<IdType, DType>(CSRToCOO(B, false), B_weights);
if (Amap.size() != Bmap.size()) return false;
for (auto itA : Amap) {
auto itB = Bmap.find(itA.first);
if (itB == Bmap.end()) return false;
if (fabs(itA.second - itB->second) >= rtol * fabs(itA.second) + atol)
return false;
}
return true;
}
template <typename IdType, typename DType>
std::pair<aten::CSRMatrix, NDArray> CSR_A(DGLContext ctx = CTX) {
// matrix([[0. , 0. , 1. , 0.7, 0. ],
// [0. , 0. , 0.5, 0.+, 0. ],
// [0.4, 0.7, 0. , 0.2, 0. ],
// [0. , 0. , 0. , 0. , 0.2]])
// (0.+ indicates that the entry exists but the value is 0.)
auto csr = aten::CSRMatrix(
4, 5, NDArray::FromVector(std::vector<IdType>({0, 2, 4, 7, 8}), ctx),
NDArray::FromVector(std::vector<IdType>({2, 3, 2, 3, 0, 1, 3, 4}), ctx),
NDArray::FromVector(std::vector<IdType>({1, 0, 2, 3, 4, 5, 6, 7}), ctx));
auto weights = NDArray::FromVector(
std::vector<DType>({0.7, 1.0, 0.5, 0.0, 0.4, 0.7, 0.2, 0.2}), ctx);
return {csr, weights};
}
template <typename IdType, typename DType>
std::pair<aten::CSRMatrix, NDArray> CSR_B(DGLContext ctx = CTX) {
// matrix([[0. , 0.9, 0. , 0.6, 0. , 0.3],
// [0. , 0. , 0. , 0. , 0. , 0.4],
// [0.+, 0. , 0. , 0. , 0. , 0.9],
// [0.8, 0.2, 0.3, 0.2, 0. , 0. ],
// [0.2, 0.4, 0. , 0. , 0. , 0. ]])
// (0.+ indicates that the entry exists but the value is 0.)
auto csr = aten::CSRMatrix(
5, 6, NDArray::FromVector(std::vector<IdType>({0, 3, 4, 6, 10, 12}), ctx),
NDArray::FromVector(
std::vector<IdType>({1, 3, 5, 5, 0, 5, 0, 1, 2, 3, 0, 1}), ctx));
auto weights = NDArray::FromVector(
std::vector<DType>(
{0.9, 0.6, 0.3, 0.4, 0.0, 0.9, 0.8, 0.2, 0.3, 0.2, 0.2, 0.4}),
ctx);
return {csr, weights};
}
template <typename IdType, typename DType>
std::pair<aten::CSRMatrix, NDArray> CSR_C(DGLContext ctx = CTX) {
// matrix([[0. , 0. , 0. , 0.2, 0. ],
// [0. , 0. , 0. , 0.5, 0.4],
// [0. , 0.2, 0. , 0.9, 0.2],
// [0. , 1. , 0. , 0.7, 0. ]])
auto csr = aten::CSRMatrix(
4, 5, NDArray::FromVector(std::vector<IdType>({0, 1, 3, 6, 8}), ctx),
NDArray::FromVector(std::vector<IdType>({3, 3, 4, 1, 3, 4, 1, 3}), ctx));
auto weights = NDArray::FromVector(
std::vector<DType>({0.2, 0.5, 0.4, 0.2, 0.9, 0.2, 1., 0.7}), ctx);
return {csr, weights};
}
template <typename IdType, typename DType>
std::pair<aten::CSRMatrix, NDArray> CSR_A_mm_B(DGLContext ctx = CTX) {
// matrix([[0.56, 0.14, 0.21, 0.14, 0. , 0.9 ],
// [0.+ , 0.+ , 0.+ , 0.+ , 0. , 0.45],
// [0.16, 0.4 , 0.06, 0.28, 0. , 0.4 ],
// [0.04, 0.08, 0. , 0. , 0. , 0. ]])
// (0.+ indicates that the entry exists but the value is 0.)
auto csr = aten::CSRMatrix(
4, 6, NDArray::FromVector(std::vector<IdType>({0, 5, 10, 15, 17}), ctx),
NDArray::FromVector(
std::vector<IdType>(
{0, 1, 2, 3, 5, 0, 1, 2, 3, 5, 0, 1, 2, 3, 5, 0, 1}),
ctx));
auto weights = NDArray::FromVector(
std::vector<DType>(
{0.56, 0.14, 0.21, 0.14, 0.9, 0., 0., 0., 0., 0.45, 0.16, 0.4, 0.06,
0.28, 0.4, 0.04, 0.08}),
ctx);
return {csr, weights};
}
template <typename IdType, typename DType>
std::pair<aten::CSRMatrix, NDArray> CSR_A_plus_C(DGLContext ctx = CTX) {
auto csr = aten::CSRMatrix(
4, 5, NDArray::FromVector(std::vector<IdType>({0, 2, 5, 9, 12}), ctx),
NDArray::FromVector(
std::vector<IdType>({2, 3, 2, 3, 4, 0, 1, 3, 4, 1, 3, 4}), ctx));
auto weights = NDArray::FromVector(
std::vector<DType>(
{1., 0.9, 0.5, 0.5, 0.4, 0.4, 0.9, 1.1, 0.2, 1., 0.7, 0.2}),
ctx);
return {csr, weights};
}
template <typename DType>
NDArray CSR_A_mask_C(DGLContext ctx = CTX) {
return NDArray::FromVector(
std::vector<DType>({0.7, 0.0, 0.0, 0.7, 0.2, 0.0, 0.0, 0.0}), ctx);
}
template <typename IdType, typename DType>
void _TestCsrmm(DGLContext ctx = CTX) {
auto A = CSR_A<IdType, DType>(ctx);
auto B = CSR_B<IdType, DType>(ctx);
auto A_mm_B = aten::CSRMM(A.first, A.second, B.first, B.second);
auto A_mm_B2 = CSR_A_mm_B<IdType, DType>(ctx);
bool result = CSRIsClose<IdType, DType>(
A_mm_B.first, A_mm_B2.first, A_mm_B.second, A_mm_B2.second, 1e-4, 1e-4);
ASSERT_TRUE(result);
}
template <typename IdType, typename DType>
void _TestCsrsum(DGLContext ctx = CTX) {
auto A = CSR_A<IdType, DType>(ctx);
auto C = CSR_C<IdType, DType>(ctx);
auto A_plus_C = aten::CSRSum({A.first, C.first}, {A.second, C.second});
auto A_plus_C2 = CSR_A_plus_C<IdType, DType>(ctx);
bool result = CSRIsClose<IdType, DType>(
A_plus_C.first, A_plus_C2.first, A_plus_C.second, A_plus_C2.second, 1e-4,
1e-4);
ASSERT_TRUE(result);
}
template <typename IdType, typename DType>
void _TestCsrmask(DGLContext ctx = CTX) {
auto A = CSR_A<IdType, DType>(ctx);
auto C = CSR_C<IdType, DType>(ctx);
auto C_coo = CSRToCOO(C.first, false);
auto A_mask_C =
aten::CSRGetData<DType>(A.first, C_coo.row, C_coo.col, A.second, 0);
auto A_mask_C2 = CSR_A_mask_C<DType>(ctx);
ASSERT_TRUE(ArrayEQ<DType>(A_mask_C, A_mask_C2));
}
TEST(CsrmmTest, TestCsrmm) {
_TestCsrmm<int32_t, float>(CPU);
_TestCsrmm<int32_t, double>(CPU);
_TestCsrmm<int64_t, float>(CPU);
_TestCsrmm<int64_t, double>(CPU);
#ifdef DGL_USE_CUDA
_TestCsrmm<int32_t, float>(GPU);
_TestCsrmm<int32_t, double>(GPU);
_TestCsrmm<int64_t, float>(GPU);
_TestCsrmm<int64_t, double>(GPU);
#endif
}
TEST(CsrmmTest, TestCsrsum) {
_TestCsrsum<int32_t, float>(CPU);
_TestCsrsum<int32_t, double>(CPU);
_TestCsrsum<int64_t, float>(CPU);
_TestCsrsum<int64_t, double>(CPU);
#ifdef DGL_USE_CUDA
_TestCsrsum<int32_t, float>(GPU);
_TestCsrsum<int32_t, double>(GPU);
_TestCsrsum<int64_t, float>(GPU);
_TestCsrsum<int64_t, double>(GPU);
#endif
}
TEST(CsrmmTest, TestCsrmask) {
_TestCsrmask<int32_t, float>(CPU);
_TestCsrmask<int32_t, double>(CPU);
_TestCsrmask<int64_t, float>(CPU);
_TestCsrmask<int64_t, double>(CPU);
#ifdef DGL_USE_CUDA
_TestCsrmask<int32_t, float>(GPU);
_TestCsrmask<int32_t, double>(GPU);
_TestCsrmask<int64_t, float>(GPU);
_TestCsrmask<int64_t, double>(GPU);
#endif
}
}; // namespace
+196
View File
@@ -0,0 +1,196 @@
#include <gtest/gtest.h>
#include "../../src/partition/ndarray_partition.h"
#include "./common.h"
using namespace dgl;
using namespace dgl::partition;
template <DGLDeviceType XPU, typename IdType>
void _TestRemainder_GeneratePermutation() {
const int64_t size = 160000;
const int num_parts = 7;
NDArrayPartitionRef part = CreatePartitionRemainderBased(size, num_parts);
IdArray idxs =
aten::Range(0, size / 10, sizeof(IdType) * 8, DGLContext{XPU, 0});
std::pair<IdArray, IdArray> result = part->GeneratePermutation(idxs);
// first part of result should be the permutation
IdArray perm = result.first.CopyTo(DGLContext{kDGLCPU, 0});
ASSERT_TRUE(perm.Ptr<IdType>() != nullptr);
ASSERT_EQ(perm->shape[0], idxs->shape[0]);
const IdType* const perm_cpu = static_cast<const IdType*>(perm->data);
// second part of result should be the counts
IdArray counts = result.second.CopyTo(DGLContext{kDGLCPU, 0});
ASSERT_TRUE(counts.Ptr<int64_t>() != nullptr);
ASSERT_EQ(counts->shape[0], num_parts);
const int64_t* const counts_cpu = static_cast<const int64_t*>(counts->data);
std::vector<int64_t> prefix(num_parts + 1, 0);
for (int p = 0; p < num_parts; ++p) {
prefix[p + 1] = prefix[p] + counts_cpu[p];
}
ASSERT_EQ(prefix.back(), idxs->shape[0]);
// copy original indexes to cpu
idxs = idxs.CopyTo(DGLContext{kDGLCPU, 0});
const IdType* const idxs_cpu = static_cast<const IdType*>(idxs->data);
for (int p = 0; p < num_parts; ++p) {
for (int64_t i = prefix[p]; i < prefix[p + 1]; ++i) {
EXPECT_EQ(idxs_cpu[perm_cpu[i]] % num_parts, p);
}
}
}
template <DGLDeviceType XPU, typename IdType>
void _TestRemainder_MapToX() {
const int64_t size = 160000;
const int num_parts = 7;
NDArrayPartitionRef part = CreatePartitionRemainderBased(size, num_parts);
for (int part_id = 0; part_id < num_parts; ++part_id) {
IdArray local = aten::Range(
0, part->PartSize(part_id), sizeof(IdType) * 8, DGLContext{XPU, 0});
IdArray global = part->MapToGlobal(local, part_id);
IdArray act_local = part->MapToLocal(global).CopyTo(CPU);
// every global index should have the same remainder as the part id
ASSERT_EQ(global->shape[0], local->shape[0]);
global = global.CopyTo(CPU);
for (int64_t i = 0; i < global->shape[0]; ++i) {
EXPECT_EQ(Ptr<IdType>(global)[i] % num_parts, part_id)
<< "i=" << i << ", num_parts=" << num_parts
<< ", part_id=" << part_id;
}
// the remapped local indices to should match the original
local = local.CopyTo(CPU);
ASSERT_EQ(local->shape[0], act_local->shape[0]);
for (int64_t i = 0; i < act_local->shape[0]; ++i) {
EXPECT_EQ(Ptr<IdType>(local)[i], Ptr<IdType>(act_local)[i]);
}
}
}
TEST(PartitionTest, TestRemainderPartition) {
#ifdef DGL_USE_CUDA
_TestRemainder_GeneratePermutation<kDGLCUDA, int32_t>();
_TestRemainder_GeneratePermutation<kDGLCUDA, int64_t>();
_TestRemainder_MapToX<kDGLCUDA, int32_t>();
_TestRemainder_MapToX<kDGLCUDA, int64_t>();
#endif
// CPU is not implemented
}
template <typename INDEX, typename RANGE>
int _FindPart(const INDEX idx, const RANGE* const range, const int num_parts) {
for (int i = 0; i < num_parts; ++i) {
if (range[i + 1] > idx) {
return i;
}
}
return -1;
}
template <DGLDeviceType XPU, typename IdType>
void _TestRange_GeneratePermutation() {
const int64_t size = 160000;
const int num_parts = 7;
IdArray range = aten::NewIdArray(
num_parts + 1, DGLContext{kDGLCPU, 0}, sizeof(IdType) * 8);
for (int i = 0; i < num_parts; ++i) {
range.Ptr<IdType>()[i] = (size / num_parts) * i;
}
range.Ptr<IdType>()[num_parts] = size;
NDArrayPartitionRef part = CreatePartitionRangeBased(
size, num_parts, range.CopyTo(DGLContext{XPU, 0}));
IdArray idxs =
aten::Range(0, size / 10, sizeof(IdType) * 8, DGLContext{XPU, 0});
std::pair<IdArray, IdArray> result = part->GeneratePermutation(idxs);
// first part of result should be the permutation
IdArray perm = result.first.CopyTo(DGLContext{kDGLCPU, 0});
ASSERT_TRUE(perm.Ptr<IdType>() != nullptr);
ASSERT_EQ(perm->shape[0], idxs->shape[0]);
const IdType* const perm_cpu = static_cast<const IdType*>(perm->data);
// second part of result should be the counts
IdArray counts = result.second.CopyTo(DGLContext{kDGLCPU, 0});
ASSERT_TRUE(counts.Ptr<int64_t>() != nullptr);
ASSERT_EQ(counts->shape[0], num_parts);
const int64_t* const counts_cpu = static_cast<const int64_t*>(counts->data);
std::vector<int64_t> prefix(num_parts + 1, 0);
for (int p = 0; p < num_parts; ++p) {
prefix[p + 1] = prefix[p] + counts_cpu[p];
}
ASSERT_EQ(prefix.back(), idxs->shape[0]);
// copy original indexes to cpu
idxs = idxs.CopyTo(DGLContext{kDGLCPU, 0});
const IdType* const idxs_cpu = static_cast<const IdType*>(idxs->data);
for (int p = 0; p < num_parts; ++p) {
for (int64_t i = prefix[p]; i < prefix[p + 1]; ++i) {
EXPECT_EQ(
_FindPart(idxs_cpu[perm_cpu[i]], range.Ptr<IdType>(), num_parts), p);
}
}
}
template <DGLDeviceType XPU, typename IdType>
void _TestRange_MapToX() {
const int64_t size = 160000;
const int num_parts = 7;
IdArray range = aten::NewIdArray(
num_parts + 1, DGLContext{kDGLCPU, 0}, sizeof(IdType) * 8);
for (int i = 0; i < num_parts; ++i) {
Ptr<IdType>(range)[i] = (size / num_parts) * i;
}
range.Ptr<IdType>()[num_parts] = size;
NDArrayPartitionRef part = CreatePartitionRangeBased(
size, num_parts, range.CopyTo(DGLContext{XPU, 0}));
for (int part_id = 0; part_id < num_parts; ++part_id) {
IdArray local = aten::Range(
0, part->PartSize(part_id), sizeof(IdType) * 8, DGLContext{XPU, 0});
IdArray global = part->MapToGlobal(local, part_id);
IdArray act_local = part->MapToLocal(global).CopyTo(CPU);
ASSERT_EQ(global->shape[0], local->shape[0]);
global = global.CopyTo(CPU);
for (int64_t i = 0; i < global->shape[0]; ++i) {
EXPECT_EQ(
_FindPart(Ptr<IdType>(global)[i], Ptr<IdType>(range), num_parts),
part_id)
<< "i=" << i << ", num_parts=" << num_parts << ", part_id=" << part_id
<< ", shape=" << global->shape[0];
}
// the remapped local indices to should match the original
local = local.CopyTo(CPU);
ASSERT_EQ(local->shape[0], act_local->shape[0]);
for (int64_t i = 0; i < act_local->shape[0]; ++i) {
EXPECT_EQ(Ptr<IdType>(local)[i], Ptr<IdType>(act_local)[i]);
}
}
}
TEST(PartitionTest, TestRangePartition) {
#ifdef DGL_USE_CUDA
_TestRange_GeneratePermutation<kDGLCUDA, int32_t>();
_TestRange_GeneratePermutation<kDGLCUDA, int64_t>();
_TestRange_MapToX<kDGLCUDA, int32_t>();
_TestRange_MapToX<kDGLCUDA, int64_t>();
#endif
// CPU is not implemented
}
+905
View File
@@ -0,0 +1,905 @@
#include <dgl/array.h>
#include <gtest/gtest.h>
#include <set>
#include <tuple>
#include "./common.h"
using namespace dgl;
using namespace dgl::runtime;
using namespace dgl::aten;
template <typename Idx>
using ETuple = std::tuple<Idx, Idx, Idx>;
template <typename Idx>
std::set<ETuple<Idx>> AllEdgeSet(bool has_data) {
if (has_data) {
std::set<ETuple<Idx>> eset;
eset.insert(ETuple<Idx>{0, 0, 2});
eset.insert(ETuple<Idx>{0, 1, 3});
eset.insert(ETuple<Idx>{1, 1, 0});
eset.insert(ETuple<Idx>{3, 2, 1});
eset.insert(ETuple<Idx>{3, 3, 4});
return eset;
} else {
std::set<ETuple<Idx>> eset;
eset.insert(ETuple<Idx>{0, 0, 0});
eset.insert(ETuple<Idx>{0, 1, 1});
eset.insert(ETuple<Idx>{1, 1, 2});
eset.insert(ETuple<Idx>{3, 2, 3});
eset.insert(ETuple<Idx>{3, 3, 4});
return eset;
}
}
template <typename Idx>
std::set<ETuple<Idx>> AllEdgePerEtypeSet(bool has_data) {
if (has_data) {
std::set<ETuple<Idx>> eset;
eset.insert(ETuple<Idx>{0, 0, 0});
eset.insert(ETuple<Idx>{0, 1, 1});
eset.insert(ETuple<Idx>{0, 2, 4});
eset.insert(ETuple<Idx>{0, 3, 6});
eset.insert(ETuple<Idx>{3, 2, 5});
eset.insert(ETuple<Idx>{3, 3, 3});
return eset;
} else {
std::set<ETuple<Idx>> eset;
eset.insert(ETuple<Idx>{0, 0, 0});
eset.insert(ETuple<Idx>{0, 1, 1});
eset.insert(ETuple<Idx>{0, 2, 2});
eset.insert(ETuple<Idx>{0, 3, 3});
eset.insert(ETuple<Idx>{3, 3, 5});
eset.insert(ETuple<Idx>{3, 2, 6});
return eset;
}
}
template <typename Idx>
std::set<ETuple<Idx>> ToEdgeSet(COOMatrix mat) {
std::set<ETuple<Idx>> eset;
Idx* row = static_cast<Idx*>(mat.row->data);
Idx* col = static_cast<Idx*>(mat.col->data);
Idx* data = static_cast<Idx*>(mat.data->data);
for (int64_t i = 0; i < mat.row->shape[0]; ++i) {
// std::cout << row[i] << " " << col[i] << " " << data[i] << std::endl;
eset.emplace(row[i], col[i], data[i]);
}
return eset;
}
template <typename Idx>
void CheckSampledResult(COOMatrix mat, IdArray rows, bool has_data) {
ASSERT_EQ(mat.num_rows, 4);
ASSERT_EQ(mat.num_cols, 4);
Idx* row = static_cast<Idx*>(mat.row->data);
Idx* col = static_cast<Idx*>(mat.col->data);
Idx* data = static_cast<Idx*>(mat.data->data);
const auto& gt = AllEdgeSet<Idx>(has_data);
for (int64_t i = 0; i < mat.row->shape[0]; ++i) {
ASSERT_TRUE(gt.count(std::make_tuple(row[i], col[i], data[i])));
ASSERT_TRUE(IsInArray(rows, row[i]));
}
}
template <typename Idx>
void CheckSampledPerEtypeResult(COOMatrix mat, IdArray rows, bool has_data) {
ASSERT_EQ(mat.num_rows, 4);
ASSERT_EQ(mat.num_cols, 4);
Idx* row = static_cast<Idx*>(mat.row->data);
Idx* col = static_cast<Idx*>(mat.col->data);
Idx* data = static_cast<Idx*>(mat.data->data);
const auto& gt = AllEdgePerEtypeSet<Idx>(has_data);
for (int64_t i = 0; i < mat.row->shape[0]; ++i) {
int64_t count = gt.count(std::make_tuple(row[i], col[i], data[i]));
ASSERT_TRUE(count);
ASSERT_TRUE(IsInArray(rows, row[i]));
}
}
template <typename Idx>
CSRMatrix CSR(bool has_data) {
IdArray indptr = NDArray::FromVector(std::vector<Idx>({0, 2, 3, 3, 5}));
IdArray indices = NDArray::FromVector(std::vector<Idx>({0, 1, 1, 2, 3}));
IdArray data = NDArray::FromVector(std::vector<Idx>({2, 3, 0, 1, 4}));
if (has_data)
return CSRMatrix(4, 4, indptr, indices, data);
else
return CSRMatrix(4, 4, indptr, indices);
}
template <typename Idx>
COOMatrix COO(bool has_data) {
IdArray row = NDArray::FromVector(std::vector<Idx>({0, 0, 1, 3, 3}));
IdArray col = NDArray::FromVector(std::vector<Idx>({0, 1, 1, 2, 3}));
IdArray data = NDArray::FromVector(std::vector<Idx>({2, 3, 0, 1, 4}));
if (has_data)
return COOMatrix(4, 4, row, col, data);
else
return COOMatrix(4, 4, row, col);
}
template <typename Idx>
std::pair<CSRMatrix, std::vector<int64_t>> CSREtypes(bool has_data) {
IdArray indptr = NDArray::FromVector(std::vector<Idx>({0, 4, 5, 5, 7}));
IdArray indices =
NDArray::FromVector(std::vector<Idx>({0, 1, 2, 3, 1, 3, 2}));
IdArray data = NDArray::FromVector(std::vector<Idx>({0, 1, 4, 6, 2, 3, 5}));
auto eid2etype_offsets = std::vector<int64_t>({0, 4, 5, 6, 7});
if (has_data)
return {CSRMatrix(4, 4, indptr, indices, data), eid2etype_offsets};
else
return {CSRMatrix(4, 4, indptr, indices), eid2etype_offsets};
}
template <typename Idx>
std::pair<COOMatrix, std::vector<int64_t>> COOEtypes(bool has_data) {
IdArray row = NDArray::FromVector(std::vector<Idx>({0, 0, 0, 0, 1, 3, 3}));
IdArray col = NDArray::FromVector(std::vector<Idx>({0, 1, 2, 3, 1, 3, 2}));
IdArray data = NDArray::FromVector(std::vector<Idx>({0, 1, 4, 6, 2, 3, 5}));
auto eid2etype_offsets = std::vector<int64_t>({0, 4, 5, 6, 7});
if (has_data)
return {COOMatrix(4, 4, row, col, data), eid2etype_offsets};
else
return {COOMatrix(4, 4, row, col), eid2etype_offsets};
}
template <typename Idx, typename FloatType>
void _TestCSRSampling(bool has_data) {
auto mat = CSR<Idx>(has_data);
FloatArray prob =
NDArray::FromVector(std::vector<FloatType>({.5, .5, .5, .5, .5}));
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWiseSampling(mat, rows, 2, prob, true);
CheckSampledResult<Idx>(rst, rows, has_data);
}
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWiseSampling(mat, rows, 2, prob, false);
CheckSampledResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
ASSERT_EQ(eset.size(), 4);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 2)));
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
}
}
prob = NDArray::FromVector(std::vector<FloatType>({.0, .5, .5, .0, .5}));
for (int k = 0; k < 100; ++k) {
auto rst = CSRRowWiseSampling(mat, rows, 2, prob, true);
CheckSampledResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
ASSERT_FALSE(eset.count(std::make_tuple(0, 1, 3)));
} else {
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_FALSE(eset.count(std::make_tuple(3, 2, 3)));
}
}
}
TEST(RowwiseTest, TestCSRSampling) {
_TestCSRSampling<int32_t, float>(true);
_TestCSRSampling<int64_t, float>(true);
_TestCSRSampling<int32_t, double>(true);
_TestCSRSampling<int64_t, double>(true);
_TestCSRSampling<int32_t, float>(false);
_TestCSRSampling<int64_t, float>(false);
_TestCSRSampling<int32_t, double>(false);
_TestCSRSampling<int64_t, double>(false);
}
template <typename Idx, typename FloatType>
void _TestCSRSamplingUniform(bool has_data) {
auto mat = CSR<Idx>(has_data);
FloatArray prob = aten::NullArray();
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWiseSampling(mat, rows, 2, prob, true);
CheckSampledResult<Idx>(rst, rows, has_data);
}
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWiseSampling(mat, rows, 2, prob, false);
CheckSampledResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 2)));
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
}
}
}
TEST(RowwiseTest, TestCSRSamplingUniform) {
_TestCSRSamplingUniform<int32_t, float>(true);
_TestCSRSamplingUniform<int64_t, float>(true);
_TestCSRSamplingUniform<int32_t, double>(true);
_TestCSRSamplingUniform<int64_t, double>(true);
_TestCSRSamplingUniform<int32_t, float>(false);
_TestCSRSamplingUniform<int64_t, float>(false);
_TestCSRSamplingUniform<int32_t, double>(false);
_TestCSRSamplingUniform<int64_t, double>(false);
}
template <typename Idx, typename FloatType>
void _TestCSRPerEtypeSampling(bool has_data) {
auto pair = CSREtypes<Idx>(has_data);
auto mat = pair.first;
auto eid2etype_offset = pair.second;
std::vector<FloatArray> prob = {
NDArray::FromVector(std::vector<FloatType>({.5, .5, .5, .5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5}))};
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
}
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, false);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(0, 2, 4));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(0, 3, 6));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 2));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 3));
ASSERT_EQ(counts, 1);
} else {
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
counts += eset.count(std::make_tuple(0, 2, 2));
counts += eset.count(std::make_tuple(0, 3, 3));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 4));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 6));
ASSERT_EQ(counts, 1);
}
}
prob = {
NDArray::FromVector(std::vector<FloatType>({.0, .5, .0, .0})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5}))};
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 0)));
} else {
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_FALSE(eset.count(std::make_tuple(0, 2, 2)));
ASSERT_FALSE(eset.count(std::make_tuple(0, 3, 3)));
}
}
}
template <typename Idx, typename FloatType>
void _TestCSRPerEtypeSamplingSorted() {
auto pair = CSREtypes<Idx>(true);
auto mat = pair.first;
auto eid2etype_offset = pair.second;
std::vector<FloatArray> prob = {
NDArray::FromVector(std::vector<FloatType>({.5, .5, .5, .5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5}))};
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, true);
}
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, false, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, true);
auto eset = ToEdgeSet<Idx>(rst);
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(0, 2, 4));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(0, 3, 6));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 2));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 3));
ASSERT_EQ(counts, 1);
}
prob = {
NDArray::FromVector(std::vector<FloatType>({.0, .5, .0, .0})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5}))};
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, true);
auto eset = ToEdgeSet<Idx>(rst);
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 0)));
}
}
TEST(RowwiseTest, TestCSRPerEtypeSampling) {
_TestCSRPerEtypeSampling<int32_t, float>(true);
_TestCSRPerEtypeSampling<int64_t, float>(true);
_TestCSRPerEtypeSampling<int32_t, double>(true);
_TestCSRPerEtypeSampling<int64_t, double>(true);
_TestCSRPerEtypeSampling<int32_t, float>(false);
_TestCSRPerEtypeSampling<int64_t, float>(false);
_TestCSRPerEtypeSampling<int32_t, double>(false);
_TestCSRPerEtypeSampling<int64_t, double>(false);
_TestCSRPerEtypeSamplingSorted<int32_t, float>();
_TestCSRPerEtypeSamplingSorted<int64_t, float>();
_TestCSRPerEtypeSamplingSorted<int32_t, double>();
_TestCSRPerEtypeSamplingSorted<int64_t, double>();
}
template <typename Idx, typename FloatType>
void _TestCSRPerEtypeSamplingUniform(bool has_data) {
auto pair = CSREtypes<Idx>(has_data);
auto mat = pair.first;
auto eid2etype_offset = pair.second;
std::vector<FloatArray> prob = {
aten::NullArray(), aten::NullArray(), aten::NullArray(),
aten::NullArray()};
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
}
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, false);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(0, 2, 4));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(0, 3, 6));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 2));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 3));
ASSERT_EQ(counts, 1);
} else {
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
counts += eset.count(std::make_tuple(0, 2, 2));
counts += eset.count(std::make_tuple(0, 3, 3));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 4));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 6));
ASSERT_EQ(counts, 1);
}
}
}
template <typename Idx, typename FloatType>
void _TestCSRPerEtypeSamplingUniformSorted() {
auto pair = CSREtypes<Idx>(true);
auto mat = pair.first;
auto eid2etype_offset = pair.second;
std::vector<FloatArray> prob = {
aten::NullArray(), aten::NullArray(), aten::NullArray(),
aten::NullArray()};
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, true);
}
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, false, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, true);
auto eset = ToEdgeSet<Idx>(rst);
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(0, 2, 4));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(0, 3, 6));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 2));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 3));
ASSERT_EQ(counts, 1);
}
}
TEST(RowwiseTest, TestCSRPerEtypeSamplingUniform) {
_TestCSRPerEtypeSamplingUniform<int32_t, float>(true);
_TestCSRPerEtypeSamplingUniform<int64_t, float>(true);
_TestCSRPerEtypeSamplingUniform<int32_t, double>(true);
_TestCSRPerEtypeSamplingUniform<int64_t, double>(true);
_TestCSRPerEtypeSamplingUniform<int32_t, float>(false);
_TestCSRPerEtypeSamplingUniform<int64_t, float>(false);
_TestCSRPerEtypeSamplingUniform<int32_t, double>(false);
_TestCSRPerEtypeSamplingUniform<int64_t, double>(false);
_TestCSRPerEtypeSamplingUniformSorted<int32_t, float>();
_TestCSRPerEtypeSamplingUniformSorted<int64_t, float>();
_TestCSRPerEtypeSamplingUniformSorted<int32_t, double>();
_TestCSRPerEtypeSamplingUniformSorted<int64_t, double>();
}
template <typename Idx, typename FloatType>
void _TestCOOSampling(bool has_data) {
auto mat = COO<Idx>(has_data);
FloatArray prob =
NDArray::FromVector(std::vector<FloatType>({.5, .5, .5, .5, .5}));
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = COORowWiseSampling(mat, rows, 2, prob, true);
CheckSampledResult<Idx>(rst, rows, has_data);
}
for (int k = 0; k < 10; ++k) {
auto rst = COORowWiseSampling(mat, rows, 2, prob, false);
CheckSampledResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
ASSERT_EQ(eset.size(), 4);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 2)));
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
}
}
prob = NDArray::FromVector(std::vector<FloatType>({.0, .5, .5, .0, .5}));
for (int k = 0; k < 100; ++k) {
auto rst = COORowWiseSampling(mat, rows, 2, prob, true);
CheckSampledResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
ASSERT_FALSE(eset.count(std::make_tuple(0, 1, 3)));
} else {
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_FALSE(eset.count(std::make_tuple(3, 2, 3)));
}
}
}
TEST(RowwiseTest, TestCOOSampling) {
_TestCOOSampling<int32_t, float>(true);
_TestCOOSampling<int64_t, float>(true);
_TestCOOSampling<int32_t, double>(true);
_TestCOOSampling<int64_t, double>(true);
_TestCOOSampling<int32_t, float>(false);
_TestCOOSampling<int64_t, float>(false);
_TestCOOSampling<int32_t, double>(false);
_TestCOOSampling<int64_t, double>(false);
}
template <typename Idx, typename FloatType>
void _TestCOOSamplingUniform(bool has_data) {
auto mat = COO<Idx>(has_data);
FloatArray prob = aten::NullArray();
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = COORowWiseSampling(mat, rows, 2, prob, true);
CheckSampledResult<Idx>(rst, rows, has_data);
}
for (int k = 0; k < 10; ++k) {
auto rst = COORowWiseSampling(mat, rows, 2, prob, false);
CheckSampledResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 2)));
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
}
}
}
TEST(RowwiseTest, TestCOOSamplingUniform) {
_TestCOOSamplingUniform<int32_t, float>(true);
_TestCOOSamplingUniform<int64_t, float>(true);
_TestCOOSamplingUniform<int32_t, double>(true);
_TestCOOSamplingUniform<int64_t, double>(true);
_TestCOOSamplingUniform<int32_t, float>(false);
_TestCOOSamplingUniform<int64_t, float>(false);
_TestCOOSamplingUniform<int32_t, double>(false);
_TestCOOSamplingUniform<int64_t, double>(false);
}
// COOPerEtypeSampling with rowwise_etype_sorted == true is not meaningful as
// it's never used in practice.
template <typename Idx, typename FloatType>
void _TestCOOPerEtypeSampling(bool has_data) {
auto pair = COOEtypes<Idx>(has_data);
auto mat = pair.first;
auto eid2etype_offset = pair.second;
std::vector<FloatArray> prob = {
NDArray::FromVector(std::vector<FloatType>({.5, .5, .5, .5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5}))};
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = COORowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
}
for (int k = 0; k < 10; ++k) {
auto rst = COORowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, false);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(0, 2, 4));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(0, 3, 6));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 2));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 3));
ASSERT_EQ(counts, 1);
} else {
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
counts += eset.count(std::make_tuple(0, 2, 2));
counts += eset.count(std::make_tuple(0, 3, 3));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 4));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 6));
ASSERT_EQ(counts, 1);
}
}
prob = {
NDArray::FromVector(std::vector<FloatType>({.0, .5, .0, .0})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5})),
NDArray::FromVector(std::vector<FloatType>({.5}))};
for (int k = 0; k < 10; ++k) {
auto rst = COORowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 0)));
} else {
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_FALSE(eset.count(std::make_tuple(0, 2, 2)));
ASSERT_FALSE(eset.count(std::make_tuple(0, 3, 3)));
}
}
}
TEST(RowwiseTest, TestCOOPerEtypeSampling) {
_TestCOOPerEtypeSampling<int32_t, float>(true);
_TestCOOPerEtypeSampling<int64_t, float>(true);
_TestCOOPerEtypeSampling<int32_t, double>(true);
_TestCOOPerEtypeSampling<int64_t, double>(true);
_TestCOOPerEtypeSampling<int32_t, float>(false);
_TestCOOPerEtypeSampling<int64_t, float>(false);
_TestCOOPerEtypeSampling<int32_t, double>(false);
_TestCOOPerEtypeSampling<int64_t, double>(false);
}
template <typename Idx, typename FloatType>
void _TestCOOPerEtypeSamplingUniform(bool has_data) {
auto pair = COOEtypes<Idx>(has_data);
auto mat = pair.first;
auto eid2etype_offset = pair.second;
std::vector<FloatArray> prob = {
aten::NullArray(), aten::NullArray(), aten::NullArray(),
aten::NullArray()};
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
for (int k = 0; k < 10; ++k) {
auto rst = COORowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, true);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
}
for (int k = 0; k < 10; ++k) {
auto rst = COORowWisePerEtypeSampling(
mat, rows, eid2etype_offset, {2, 2, 2, 2}, prob, false);
CheckSampledPerEtypeResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(0, 2, 4));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(0, 3, 6));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 2));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 3));
ASSERT_EQ(counts, 1);
} else {
int counts = 0;
counts += eset.count(std::make_tuple(0, 0, 0));
counts += eset.count(std::make_tuple(0, 1, 1));
counts += eset.count(std::make_tuple(0, 2, 2));
counts += eset.count(std::make_tuple(0, 3, 3));
ASSERT_EQ(counts, 2);
counts = 0;
counts += eset.count(std::make_tuple(1, 1, 4));
ASSERT_EQ(counts, 0);
counts = 0;
counts += eset.count(std::make_tuple(3, 3, 5));
ASSERT_EQ(counts, 1);
counts = 0;
counts += eset.count(std::make_tuple(3, 2, 6));
ASSERT_EQ(counts, 1);
}
}
}
TEST(RowwiseTest, TestCOOPerEtypeSamplingUniform) {
_TestCOOPerEtypeSamplingUniform<int32_t, float>(true);
_TestCOOPerEtypeSamplingUniform<int64_t, float>(true);
_TestCOOPerEtypeSamplingUniform<int32_t, double>(true);
_TestCOOPerEtypeSamplingUniform<int64_t, double>(true);
_TestCOOPerEtypeSamplingUniform<int32_t, float>(false);
_TestCOOPerEtypeSamplingUniform<int64_t, float>(false);
_TestCOOPerEtypeSamplingUniform<int32_t, double>(false);
_TestCOOPerEtypeSamplingUniform<int64_t, double>(false);
}
template <typename Idx, typename FloatType>
void _TestCSRTopk(bool has_data) {
auto mat = CSR<Idx>(has_data);
FloatArray weight =
NDArray::FromVector(std::vector<FloatType>({.1f, .0f, -.1f, .2f, .5f}));
// -.1, .2, .1, .0, .5
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
{
auto rst = CSRRowWiseTopk(mat, rows, 1, weight, true);
auto eset = ToEdgeSet<Idx>(rst);
ASSERT_EQ(eset.size(), 2);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 2)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 1)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 3)));
}
}
{
auto rst = CSRRowWiseTopk(mat, rows, 1, weight, false);
auto eset = ToEdgeSet<Idx>(rst);
ASSERT_EQ(eset.size(), 2);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
}
}
}
TEST(RowwiseTest, TestCSRTopk) {
_TestCSRTopk<int32_t, float>(true);
_TestCSRTopk<int64_t, float>(true);
_TestCSRTopk<int32_t, double>(true);
_TestCSRTopk<int64_t, double>(true);
_TestCSRTopk<int32_t, float>(false);
_TestCSRTopk<int64_t, float>(false);
_TestCSRTopk<int32_t, double>(false);
_TestCSRTopk<int64_t, double>(false);
}
template <typename Idx, typename FloatType>
void _TestCOOTopk(bool has_data) {
auto mat = COO<Idx>(has_data);
FloatArray weight =
NDArray::FromVector(std::vector<FloatType>({.1f, .0f, -.1f, .2f, .5f}));
// -.1, .2, .1, .0, .5
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 3}));
{
auto rst = COORowWiseTopk(mat, rows, 1, weight, true);
auto eset = ToEdgeSet<Idx>(rst);
ASSERT_EQ(eset.size(), 2);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 2)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 1)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 2, 3)));
}
}
{
auto rst = COORowWiseTopk(mat, rows, 1, weight, false);
auto eset = ToEdgeSet<Idx>(rst);
ASSERT_EQ(eset.size(), 2);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
}
}
}
TEST(RowwiseTest, TestCOOTopk) {
_TestCOOTopk<int32_t, float>(true);
_TestCOOTopk<int64_t, float>(true);
_TestCOOTopk<int32_t, double>(true);
_TestCOOTopk<int64_t, double>(true);
_TestCOOTopk<int32_t, float>(false);
_TestCOOTopk<int64_t, float>(false);
_TestCOOTopk<int32_t, double>(false);
_TestCOOTopk<int64_t, double>(false);
}
template <typename Idx, typename FloatType>
void _TestCSRSamplingBiased(bool has_data) {
auto mat = CSR<Idx>(has_data);
// 0 - 0,1
// 1 - 1
// 3 - 2,3
NDArray tag_offset = NDArray::FromVector(
std::vector<Idx>({0, 1, 2, 0, 0, 1, 0, 0, 0, 0, 1, 2}));
tag_offset = tag_offset.CreateView({4, 3}, tag_offset->dtype);
IdArray rows = NDArray::FromVector(std::vector<Idx>({0, 1, 3}));
FloatArray bias = NDArray::FromVector(std::vector<FloatType>({0, 0.5}));
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWiseSamplingBiased(mat, rows, 1, tag_offset, bias, false);
CheckSampledResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(1, 1, 0)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(1, 1, 2)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
}
}
for (int k = 0; k < 10; ++k) {
auto rst = CSRRowWiseSamplingBiased(mat, rows, 3, tag_offset, bias, true);
CheckSampledResult<Idx>(rst, rows, has_data);
auto eset = ToEdgeSet<Idx>(rst);
if (has_data) {
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 3)));
ASSERT_TRUE(eset.count(std::make_tuple(1, 1, 0)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 2)));
ASSERT_FALSE(eset.count(std::make_tuple(3, 2, 1)));
} else {
ASSERT_TRUE(eset.count(std::make_tuple(0, 1, 1)));
ASSERT_TRUE(eset.count(std::make_tuple(1, 1, 2)));
ASSERT_TRUE(eset.count(std::make_tuple(3, 3, 4)));
ASSERT_FALSE(eset.count(std::make_tuple(0, 0, 0)));
ASSERT_FALSE(eset.count(std::make_tuple(3, 2, 3)));
}
}
}
TEST(RowwiseTest, TestCSRSamplingBiased) {
_TestCSRSamplingBiased<int32_t, float>(true);
_TestCSRSamplingBiased<int32_t, float>(false);
_TestCSRSamplingBiased<int64_t, float>(true);
_TestCSRSamplingBiased<int64_t, float>(false);
_TestCSRSamplingBiased<int32_t, double>(true);
_TestCSRSamplingBiased<int32_t, double>(false);
_TestCSRSamplingBiased<int64_t, double>(true);
_TestCSRSamplingBiased<int64_t, double>(false);
}
+280
View File
@@ -0,0 +1,280 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include "../../src/random/cpu/sample_utils.h"
#include "./common.h"
using namespace dgl;
using namespace dgl::aten;
// TODO: adapt this to Random::Choice
template <typename Idx, typename DType>
void _TestWithReplacement(RandomEngine* re) {
Idx n_categories = 100;
Idx n_rolls = 1000000;
std::vector<DType> _prob;
DType accum = 0.;
for (Idx i = 0; i < n_categories; ++i) {
_prob.push_back(re->Uniform<DType>());
accum += _prob.back();
}
for (Idx i = 0; i < n_categories; ++i) _prob[i] /= accum;
FloatArray prob = NDArray::FromVector(_prob);
auto _check_given_sampler = [n_categories, n_rolls,
&_prob](utils::BaseSampler<Idx>* s) {
std::vector<Idx> counter(n_categories, 0);
for (Idx i = 0; i < n_rolls; ++i) {
Idx dice = s->Draw();
counter[dice]++;
}
for (Idx i = 0; i < n_categories; ++i)
ASSERT_NEAR(static_cast<DType>(counter[i]) / n_rolls, _prob[i], 1e-2);
};
auto _check_random_choice = [n_categories, n_rolls, &_prob, prob]() {
std::vector<int64_t> counter(n_categories, 0);
for (Idx i = 0; i < n_rolls; ++i) {
Idx dice = RandomEngine::ThreadLocal()->Choice<int64_t>(prob);
counter[dice]++;
}
for (Idx i = 0; i < n_categories; ++i)
ASSERT_NEAR(static_cast<DType>(counter[i]) / n_rolls, _prob[i], 1e-2);
};
utils::AliasSampler<Idx, DType, true> as(re, prob);
utils::CDFSampler<Idx, DType, true> cs(re, prob);
utils::TreeSampler<Idx, DType, true> ts(re, prob);
_check_given_sampler(&as);
_check_given_sampler(&cs);
_check_given_sampler(&ts);
_check_random_choice();
}
TEST(SampleUtilsTest, TestWithReplacement) {
RandomEngine* re = RandomEngine::ThreadLocal();
re->SetSeed(42);
_TestWithReplacement<int32_t, float>(re);
re->SetSeed(42);
_TestWithReplacement<int32_t, double>(re);
re->SetSeed(42);
_TestWithReplacement<int64_t, float>(re);
re->SetSeed(42);
_TestWithReplacement<int64_t, double>(re);
};
template <typename Idx, typename DType>
void _TestWithoutReplacementOrder(RandomEngine* re) {
// TODO(BarclayII): is there a reliable way to do this test?
std::vector<DType> _prob = {1e6f, 1e-6f, 1e-2f, 1e2f};
FloatArray prob = NDArray::FromVector(_prob);
std::vector<Idx> ground_truth = {0, 3, 2, 1};
auto _check_given_sampler = [&ground_truth](utils::BaseSampler<Idx>* s) {
for (size_t i = 0; i < ground_truth.size(); ++i) {
Idx dice = s->Draw();
ASSERT_EQ(dice, ground_truth[i]);
}
};
utils::AliasSampler<Idx, DType, false> as(re, prob);
utils::CDFSampler<Idx, DType, false> cs(re, prob);
utils::TreeSampler<Idx, DType, false> ts(re, prob);
_check_given_sampler(&as);
_check_given_sampler(&cs);
_check_given_sampler(&ts);
}
TEST(SampleUtilsTest, TestWithoutReplacementOrder) {
RandomEngine* re = RandomEngine::ThreadLocal();
re->SetSeed(42);
_TestWithoutReplacementOrder<int32_t, float>(re);
re->SetSeed(42);
_TestWithoutReplacementOrder<int32_t, double>(re);
re->SetSeed(42);
_TestWithoutReplacementOrder<int64_t, float>(re);
re->SetSeed(42);
_TestWithoutReplacementOrder<int64_t, double>(re);
};
template <typename Idx, typename DType>
void _TestWithoutReplacementUnique(RandomEngine* re) {
Idx N = 1000000;
std::vector<DType> _likelihood;
for (Idx i = 0; i < N; ++i) _likelihood.push_back(re->Uniform<DType>());
FloatArray likelihood = NDArray::FromVector(_likelihood);
auto _check_given_sampler = [N](utils::BaseSampler<Idx>* s) {
std::vector<int> cnt(N, 0);
for (Idx i = 0; i < N; ++i) {
Idx dice = s->Draw();
cnt[dice]++;
}
for (Idx i = 0; i < N; ++i) ASSERT_EQ(cnt[i], 1);
};
utils::AliasSampler<Idx, DType, false> as(re, likelihood);
utils::CDFSampler<Idx, DType, false> cs(re, likelihood);
utils::TreeSampler<Idx, DType, false> ts(re, likelihood);
_check_given_sampler(&as);
_check_given_sampler(&cs);
_check_given_sampler(&ts);
}
TEST(SampleUtilsTest, TestWithoutReplacementUnique) {
RandomEngine* re = RandomEngine::ThreadLocal();
re->SetSeed(42);
_TestWithoutReplacementUnique<int32_t, float>(re);
re->SetSeed(42);
_TestWithoutReplacementUnique<int32_t, double>(re);
re->SetSeed(42);
_TestWithoutReplacementUnique<int64_t, float>(re);
re->SetSeed(42);
_TestWithoutReplacementUnique<int64_t, double>(re);
};
template <typename Idx, typename DType>
void _TestChoice(RandomEngine* re) {
re->SetSeed(42);
std::vector<DType> prob_vec = {1., 0., 0., 0., 2., 2., 0., 0.};
FloatArray prob = FloatArray::FromVector(prob_vec);
{
for (int k = 0; k < 1000; ++k) {
Idx x = re->Choice<Idx>(prob);
ASSERT_TRUE(x == 0 || x == 4 || x == 5);
}
}
// num = 0
{
IdArray rst = re->Choice<Idx, DType>(0, prob, true);
ASSERT_EQ(rst->shape[0], 0);
}
// w/ replacement
{
IdArray rst = re->Choice<Idx, DType>(1000, prob, true);
ASSERT_EQ(rst->shape[0], 1000);
for (int64_t i = 0; i < 1000; ++i) {
Idx x = static_cast<Idx*>(rst->data)[i];
ASSERT_TRUE(x == 0 || x == 4 || x == 5);
}
}
// w/o replacement
{
IdArray rst = re->Choice<Idx, DType>(3, prob, false);
ASSERT_EQ(rst->shape[0], 3);
std::set<Idx> idxset;
for (int64_t i = 0; i < 3; ++i) {
Idx x = static_cast<Idx*>(rst->data)[i];
idxset.insert(x);
}
ASSERT_EQ(idxset.size(), 3);
ASSERT_EQ(idxset.count(0), 1);
ASSERT_EQ(idxset.count(4), 1);
ASSERT_EQ(idxset.count(5), 1);
}
}
TEST(RandomTest, TestChoice) {
RandomEngine* re = RandomEngine::ThreadLocal();
_TestChoice<int32_t, float>(re);
_TestChoice<int64_t, float>(re);
_TestChoice<int32_t, double>(re);
_TestChoice<int64_t, double>(re);
}
template <typename Idx>
void _TestUniformChoice(RandomEngine* re) {
re->SetSeed(42);
// num == 0
{
IdArray rst = re->UniformChoice<Idx>(0, 100, true);
ASSERT_EQ(rst->shape[0], 0);
}
// w/ replacement
{
IdArray rst = re->UniformChoice<Idx>(1000, 100, true);
ASSERT_EQ(rst->shape[0], 1000);
for (int64_t i = 0; i < 1000; ++i) {
Idx x = static_cast<Idx*>(rst->data)[i];
ASSERT_TRUE(x >= 0 && x < 100);
}
}
// w/o replacement
{
IdArray rst = re->UniformChoice<Idx>(99, 100, false);
ASSERT_EQ(rst->shape[0], 99);
std::set<Idx> idxset;
for (int64_t i = 0; i < 99; ++i) {
Idx x = static_cast<Idx*>(rst->data)[i];
ASSERT_TRUE(x >= 0 && x < 100);
idxset.insert(x);
}
ASSERT_EQ(idxset.size(), 99);
}
}
TEST(RandomTest, TestUniformChoice) {
RandomEngine* re = RandomEngine::ThreadLocal();
_TestUniformChoice<int32_t>(re);
_TestUniformChoice<int64_t>(re);
_TestUniformChoice<int32_t>(re);
_TestUniformChoice<int64_t>(re);
}
template <typename Idx, typename FloatType>
void _TestBiasedChoice(RandomEngine* re) {
re->SetSeed(42);
// num == 0
{
Idx split[] = {0, 1, 2};
FloatArray bias = NDArray::FromVector(std::vector<FloatType>({1, 3}));
IdArray rst = re->BiasedChoice<Idx, FloatType>(0, split, bias, true);
ASSERT_EQ(rst->shape[0], 0);
}
// basic test
{
Idx sample_num = 100000;
Idx population = 1000000;
Idx split[] = {0, population / 2, population};
FloatArray bias = NDArray::FromVector(std::vector<FloatType>({1, 3}));
IdArray rst =
re->BiasedChoice<Idx, FloatType>(sample_num, split, bias, true);
auto rst_data = static_cast<Idx*>(rst->data);
Idx larger = 0;
for (Idx i = 0; i < sample_num; ++i)
if (rst_data[i] >= population / 2) larger++;
ASSERT_LE(fabs((double)larger / sample_num - 0.75), 1e-2);
}
// without replacement
{
Idx sample_num = 500;
Idx population = 1000;
Idx split[] = {0, sample_num, population};
FloatArray bias = NDArray::FromVector(std::vector<FloatType>({1, 0}));
IdArray rst =
re->BiasedChoice<Idx, FloatType>(sample_num, split, bias, false);
auto rst_data = static_cast<Idx*>(rst->data);
std::set<Idx> idxset;
for (int64_t i = 0; i < sample_num; ++i) {
Idx x = rst_data[i];
ASSERT_LT(x, sample_num);
idxset.insert(x);
}
ASSERT_EQ(idxset.size(), sample_num);
}
}
TEST(RandomTest, TestBiasedChoice) {
RandomEngine* re = RandomEngine::ThreadLocal();
_TestBiasedChoice<int32_t, float>(re);
_TestBiasedChoice<int64_t, float>(re);
_TestBiasedChoice<int32_t, double>(re);
_TestBiasedChoice<int64_t, double>(re);
}
+103
View File
@@ -0,0 +1,103 @@
#include <dgl/graph_serializer.h>
#include <dgl/immutable_graph.h>
#include <dmlc/memory_io.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>
#include "../../src/graph/heterograph.h"
#include "../../src/graph/unit_graph.h"
#include "./common.h"
using namespace dgl;
using namespace dgl::aten;
using namespace dmlc;
TEST(Serialize, UnitGraph_COO) {
aten::CSRMatrix csr_matrix;
auto src = VecToIdArray<int64_t>({1, 2, 5, 3});
auto dst = VecToIdArray<int64_t>({1, 6, 2, 6});
auto mg = std::dynamic_pointer_cast<UnitGraph>(
dgl::UnitGraph::CreateFromCOO(2, 9, 8, src, dst, COO_CODE));
std::string blob;
dmlc::MemoryStringStream ifs(&blob);
static_cast<dmlc::Stream *>(&ifs)->Write(mg);
dmlc::MemoryStringStream ofs(&blob);
auto ug2 = Serializer::make_shared<UnitGraph>();
static_cast<dmlc::Stream *>(&ofs)->Read(&ug2);
EXPECT_EQ(ug2->NumVertices(0), 9);
EXPECT_EQ(ug2->NumVertices(1), 8);
EXPECT_EQ(ug2->NumEdges(0), 4);
EXPECT_EQ(ug2->FindEdge(0, 1).first, 2);
EXPECT_EQ(ug2->FindEdge(0, 1).second, 6);
}
TEST(Serialize, UnitGraph_CSR) {
aten::CSRMatrix csr_matrix;
auto src = VecToIdArray<int64_t>({1, 2, 5, 3});
auto dst = VecToIdArray<int64_t>({1, 6, 2, 6});
auto coo_g = std::dynamic_pointer_cast<UnitGraph>(
dgl::UnitGraph::CreateFromCOO(2, 9, 8, src, dst));
auto csr_g =
std::dynamic_pointer_cast<UnitGraph>(coo_g->GetGraphInFormat(CSR_CODE));
std::string blob;
dmlc::MemoryStringStream ifs(&blob);
static_cast<dmlc::Stream *>(&ifs)->Write(csr_g);
dmlc::MemoryStringStream ofs(&blob);
auto ug2 = Serializer::make_shared<UnitGraph>();
static_cast<dmlc::Stream *>(&ofs)->Read(&ug2);
// Query operation is not supported on CSR, how to check it?
}
TEST(Serialize, ImmutableGraph) {
auto src = VecToIdArray<int64_t>({1, 2, 5, 3});
auto dst = VecToIdArray<int64_t>({1, 6, 2, 6});
auto gptr = ImmutableGraph::CreateFromCOO(10, src, dst);
std::string blob;
dmlc::MemoryStringStream ifs(&blob);
static_cast<dmlc::Stream *>(&ifs)->Write(gptr);
dmlc::MemoryStringStream ofs(&blob);
auto rptr_read = dgl::Serializer::make_shared<ImmutableGraph>();
static_cast<dmlc::Stream *>(&ofs)->Read(&rptr_read);
EXPECT_EQ(rptr_read->NumEdges(), 4);
EXPECT_EQ(rptr_read->NumVertices(), 10);
EXPECT_EQ(rptr_read->FindEdge(2).first, 5);
EXPECT_EQ(rptr_read->FindEdge(2).second, 2);
}
TEST(Serialize, HeteroGraph) {
auto src = VecToIdArray<int64_t>({1, 2, 5, 3});
auto dst = VecToIdArray<int64_t>({1, 6, 2, 6});
auto mg1 = dgl::UnitGraph::CreateFromCOO(2, 9, 8, src, dst);
src = VecToIdArray<int64_t>({6, 2, 5, 1, 8});
dst = VecToIdArray<int64_t>({5, 2, 4, 8, 0});
auto mg2 = dgl::UnitGraph::CreateFromCOO(1, 9, 9, src, dst);
std::vector<HeteroGraphPtr> relgraphs;
relgraphs.push_back(mg1);
relgraphs.push_back(mg2);
src = VecToIdArray<int64_t>({0, 0});
dst = VecToIdArray<int64_t>({1, 0});
auto meta_gptr = ImmutableGraph::CreateFromCOO(3, src, dst);
auto hrptr = std::make_shared<HeteroGraph>(meta_gptr, relgraphs);
std::string blob;
dmlc::MemoryStringStream ifs(&blob);
static_cast<dmlc::Stream *>(&ifs)->Write(hrptr);
dmlc::MemoryStringStream ofs(&blob);
auto gptr = dgl::Serializer::make_shared<HeteroGraph>();
static_cast<dmlc::Stream *>(&ofs)->Read(&gptr);
EXPECT_EQ(gptr->NumVertices(0), 9);
EXPECT_EQ(gptr->NumVertices(1), 8);
}
+100
View File
@@ -0,0 +1,100 @@
#include <dgl/runtime/serializer.h>
#include <dgl/runtime/smart_ptr_serializer.h>
#include <dmlc/io.h>
#include <dmlc/logging.h>
#include <dmlc/memory_io.h>
#include <dmlc/parameter.h>
#include <gtest/gtest.h>
#include <cstring>
#include <iostream>
#include <sstream>
#include <unordered_map>
using namespace std;
class MyClass {
public:
MyClass() {}
MyClass(std::string data) : data_(data) {}
inline void Save(dmlc::Stream *strm) const { strm->Write(this->data_); }
inline bool Load(dmlc::Stream *strm) { return strm->Read(&data_); }
inline bool operator==(const MyClass &other) const {
return data_ == other.data_;
}
public:
std::string data_;
};
// need to declare the traits property of my class to dmlc
namespace dmlc {
DMLC_DECLARE_TRAITS(has_saveload, MyClass, true);
}
template <typename T>
class SmartPtrTest : public ::testing::Test {
public:
typedef T SmartPtr;
};
using SmartPtrTypes =
::testing::Types<std::shared_ptr<MyClass>, std::unique_ptr<MyClass>>;
TYPED_TEST_SUITE(SmartPtrTest, SmartPtrTypes);
TYPED_TEST(SmartPtrTest, Obj_Test) {
std::string blob;
dmlc::MemoryStringStream fs(&blob);
using SmartPtr = typename TestFixture::SmartPtr;
auto myc = SmartPtr(new MyClass("1111"));
{ static_cast<dmlc::Stream *>(&fs)->Write(myc); }
fs.Seek(0);
auto copy_data = SmartPtr(new MyClass());
CHECK(static_cast<dmlc::Stream *>(&fs)->Read(&copy_data));
EXPECT_EQ(myc->data_, copy_data->data_);
}
TYPED_TEST(SmartPtrTest, Vector_Test1) {
std::string blob;
dmlc::MemoryStringStream fs(&blob);
using SmartPtr = typename TestFixture::SmartPtr;
typedef std::pair<std::string, SmartPtr> Pair;
std::vector<Pair> myclasses;
myclasses.emplace_back("a", SmartPtr(new MyClass("@A@B")));
myclasses.emplace_back("b", SmartPtr(new MyClass("2222")));
static_cast<dmlc::Stream *>(&fs)->Write<std::vector<Pair>>(myclasses);
dmlc::MemoryStringStream ofs(&blob);
std::vector<Pair> copy_myclasses;
static_cast<dmlc::Stream *>(&ofs)->Read<std::vector<Pair>>(&copy_myclasses);
EXPECT_TRUE(std::equal(
myclasses.begin(), myclasses.end(), copy_myclasses.begin(),
[](const Pair &left, const Pair &right) {
return (left.second->data_ == right.second->data_) &&
(left.first == right.first);
}));
}
TYPED_TEST(SmartPtrTest, Vector_Test2) {
std::string blob;
dmlc::MemoryStringStream fs(&blob);
using SmartPtr = typename TestFixture::SmartPtr;
std::vector<SmartPtr> myclasses;
myclasses.emplace_back(new MyClass("@A@"));
myclasses.emplace_back(new MyClass("2222"));
static_cast<dmlc::Stream *>(&fs)->Write<std::vector<SmartPtr>>(myclasses);
dmlc::MemoryStringStream ofs(&blob);
std::vector<SmartPtr> copy_myclasses;
static_cast<dmlc::Stream *>(&ofs)->Read<std::vector<SmartPtr>>(
&copy_myclasses);
EXPECT_TRUE(std::equal(
myclasses.begin(), myclasses.end(), copy_myclasses.begin(),
[](const SmartPtr &left, const SmartPtr &right) {
return left->data_ == right->data_;
}));
}
+576
View File
@@ -0,0 +1,576 @@
#include <dgl/array.h>
#include <dmlc/omp.h>
#include <gtest/gtest.h>
#include <omp.h>
#include <random>
#include "./common.h"
using namespace dgl;
using namespace dgl::runtime;
namespace {
template <typename IDX>
aten::CSRMatrix CSR1(DGLContext ctx = CTX) {
// [[0, 1, 1, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 3, 1, 4]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 3, 5, 5}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 2, 3, 4, 1}), sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::CSRMatrix CSR2(DGLContext ctx = CTX) {
// has duplicate entries
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 5, 3, 1, 4]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 3, 4, 6, 6}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 2, 5, 3, 1, 4}), sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::COOMatrix COO1(DGLContext ctx = CTX) {
// [[0, 1, 1, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 3, 1, 4]
// row : [0, 2, 0, 1, 2]
// col : [1, 2, 2, 0, 3]
return aten::COOMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 0, 1, 2}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 3, 1, 2, 4}), sizeof(IDX) * 8, ctx));
}
template <typename IDX>
aten::COOMatrix COO2(DGLContext ctx = CTX) {
// has duplicate entries
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 5, 3, 1, 4]
// row : [0, 2, 0, 1, 2, 0]
// col : [1, 2, 2, 0, 3, 2]
return aten::COOMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 0, 1, 2, 0}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 3, 2}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 1, 2, 3, 4, 5}), sizeof(IDX) * 8, ctx));
}
template <typename IDX>
aten::CSRMatrix SR_CSR3(DGLContext ctx) {
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 3, 4, 6, 6}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({2, 1, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 2, 5, 3, 1, 4}), sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::CSRMatrix SRC_CSR3(DGLContext ctx) {
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 3, 4, 6, 6}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({2, 0, 5, 3, 1, 4}), sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::COOMatrix COO3(DGLContext ctx) {
// has duplicate entries
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// row : [0, 2, 0, 1, 2, 0]
// col : [2, 2, 1, 0, 3, 2]
return aten::COOMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 0, 1, 2, 0}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({2, 2, 1, 0, 3, 2}), sizeof(IDX) * 8, ctx));
}
template <typename IDX>
aten::COOMatrix COORandomized(IDX rows_and_cols, int64_t nnz, int seed) {
std::vector<IDX> vec_rows(nnz);
std::vector<IDX> vec_cols(nnz);
std::vector<IDX> vec_data(nnz);
#pragma omp parallel
{
const int64_t num_threads = omp_get_num_threads();
const int64_t thread_id = omp_get_thread_num();
const int64_t chunk = nnz / num_threads;
const int64_t size = (thread_id == num_threads - 1)
? nnz - chunk * (num_threads - 1)
: chunk;
auto rows = vec_rows.data() + thread_id * chunk;
auto cols = vec_cols.data() + thread_id * chunk;
auto data = vec_data.data() + thread_id * chunk;
std::mt19937_64 gen64(seed + thread_id);
std::mt19937 gen32(seed + thread_id);
for (int64_t i = 0; i < size; ++i) {
rows[i] = gen64() % rows_and_cols;
cols[i] = gen64() % rows_and_cols;
data[i] = gen32() % 90 + 1;
}
}
return aten::COOMatrix(
rows_and_cols, rows_and_cols,
aten::VecToIdArray(vec_rows, sizeof(IDX) * 8, CTX),
aten::VecToIdArray(vec_cols, sizeof(IDX) * 8, CTX),
aten::VecToIdArray(vec_data, sizeof(IDX) * 8, CTX), false, false);
}
struct SparseCOOCSR {
static constexpr uint64_t NUM_ROWS = 100;
static constexpr uint64_t NUM_COLS = 150;
static constexpr uint64_t NUM_NZ = 5;
template <typename IDX>
static aten::COOMatrix COOSparse(const DGLContext &ctx = CTX) {
return aten::COOMatrix(
NUM_ROWS, NUM_COLS,
aten::VecToIdArray(
std::vector<IDX>({0, 1, 2, 3, 4}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 3, 4, 5}), sizeof(IDX) * 8, ctx));
}
template <typename IDX>
static aten::CSRMatrix CSRSparse(const DGLContext &ctx = CTX) {
auto &&indptr = std::vector<IDX>(NUM_ROWS + 1, NUM_NZ);
for (size_t i = 0; i < NUM_NZ; ++i) {
indptr[i + 1] = static_cast<IDX>(i + 1);
}
indptr[0] = 0;
return aten::CSRMatrix(
NUM_ROWS, NUM_COLS, aten::VecToIdArray(indptr, sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 3, 4, 5}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 1, 1, 1, 1}), sizeof(IDX) * 8, ctx),
false);
}
};
template <typename IDX>
aten::COOMatrix RowSorted_NullData_COO(DGLContext ctx = CTX) {
// [[0, 1, 1, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// row : [0, 0, 1, 2, 2]
// col : [1, 2, 0, 2, 3]
return aten::COOMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 0, 1, 2, 2}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::NullArray(), true, false);
}
template <typename IDX>
aten::CSRMatrix RowSorted_NullData_CSR(DGLContext ctx = CTX) {
// [[0, 1, 1, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 1, 2, 3, 4]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 3, 5, 5}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 1, 2, 3, 4}), sizeof(IDX) * 8, ctx),
false);
}
} // namespace
template <typename IDX>
void _TestCOOToCSR(DGLContext ctx) {
auto coo = COO1<IDX>(ctx);
auto csr = CSR1<IDX>(ctx);
auto tcsr = aten::COOToCSR(coo);
ASSERT_FALSE(coo.row_sorted);
ASSERT_EQ(csr.num_rows, tcsr.num_rows);
ASSERT_EQ(csr.num_cols, tcsr.num_cols);
ASSERT_TRUE(ArrayEQ<IDX>(csr.indptr, tcsr.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(csr.indices, tcsr.indices));
coo = COO2<IDX>(ctx);
csr = CSR2<IDX>(ctx);
tcsr = aten::COOToCSR(coo);
ASSERT_EQ(coo.num_rows, csr.num_rows);
ASSERT_EQ(coo.num_cols, csr.num_cols);
ASSERT_TRUE(ArrayEQ<IDX>(csr.indptr, tcsr.indptr));
// Convert from row sorted coo
coo = COO1<IDX>(ctx);
auto rs_coo = aten::COOSort(coo, false);
auto rs_csr = CSR1<IDX>(ctx);
auto rs_tcsr = aten::COOToCSR(rs_coo);
ASSERT_TRUE(rs_coo.row_sorted);
ASSERT_EQ(coo.num_rows, rs_tcsr.num_rows);
ASSERT_EQ(coo.num_cols, rs_tcsr.num_cols);
ASSERT_TRUE(ArrayEQ<IDX>(rs_csr.indptr, rs_tcsr.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(rs_tcsr.indices, rs_coo.col));
ASSERT_TRUE(ArrayEQ<IDX>(rs_tcsr.data, rs_coo.data));
coo = COO3<IDX>(ctx);
rs_coo = aten::COOSort(coo, false);
rs_csr = SR_CSR3<IDX>(ctx);
rs_tcsr = aten::COOToCSR(rs_coo);
ASSERT_EQ(coo.num_rows, rs_tcsr.num_rows);
ASSERT_EQ(coo.num_cols, rs_tcsr.num_cols);
ASSERT_TRUE(ArrayEQ<IDX>(rs_csr.indptr, rs_tcsr.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(rs_tcsr.indices, rs_coo.col));
ASSERT_TRUE(ArrayEQ<IDX>(rs_tcsr.data, rs_coo.data));
rs_coo = RowSorted_NullData_COO<IDX>(ctx);
ASSERT_TRUE(rs_coo.row_sorted);
rs_csr = RowSorted_NullData_CSR<IDX>(ctx);
rs_tcsr = aten::COOToCSR(rs_coo);
ASSERT_EQ(coo.num_rows, rs_tcsr.num_rows);
ASSERT_EQ(rs_csr.num_rows, rs_tcsr.num_rows);
ASSERT_EQ(coo.num_cols, rs_tcsr.num_cols);
ASSERT_EQ(rs_csr.num_cols, rs_tcsr.num_cols);
ASSERT_TRUE(ArrayEQ<IDX>(rs_csr.indptr, rs_tcsr.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(rs_csr.indices, rs_tcsr.indices));
ASSERT_TRUE(ArrayEQ<IDX>(rs_csr.data, rs_tcsr.data));
ASSERT_TRUE(ArrayEQ<IDX>(rs_coo.col, rs_tcsr.indices));
ASSERT_FALSE(ArrayEQ<IDX>(rs_coo.data, rs_tcsr.data));
// Convert from col sorted coo
coo = COO1<IDX>(ctx);
auto src_coo = aten::COOSort(coo, true);
auto src_csr = CSR1<IDX>(ctx);
auto src_tcsr = aten::COOToCSR(src_coo);
ASSERT_EQ(coo.num_rows, src_tcsr.num_rows);
ASSERT_EQ(coo.num_cols, src_tcsr.num_cols);
ASSERT_TRUE(src_tcsr.sorted);
ASSERT_TRUE(ArrayEQ<IDX>(src_tcsr.indptr, src_csr.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(src_tcsr.indices, src_coo.col));
ASSERT_TRUE(ArrayEQ<IDX>(src_tcsr.data, src_coo.data));
coo = COO3<IDX>(ctx);
src_coo = aten::COOSort(coo, true);
src_csr = SRC_CSR3<IDX>(ctx);
src_tcsr = aten::COOToCSR(src_coo);
ASSERT_EQ(coo.num_rows, src_tcsr.num_rows);
ASSERT_EQ(coo.num_cols, src_tcsr.num_cols);
ASSERT_TRUE(src_tcsr.sorted);
ASSERT_TRUE(ArrayEQ<IDX>(src_tcsr.indptr, src_csr.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(src_tcsr.indices, src_coo.col));
ASSERT_TRUE(ArrayEQ<IDX>(src_tcsr.data, src_coo.data));
coo = SparseCOOCSR::COOSparse<IDX>(ctx);
csr = SparseCOOCSR::CSRSparse<IDX>(ctx);
tcsr = aten::COOToCSR(coo);
ASSERT_FALSE(coo.row_sorted);
ASSERT_EQ(csr.num_rows, tcsr.num_rows);
ASSERT_EQ(csr.num_cols, tcsr.num_cols);
ASSERT_TRUE(ArrayEQ<IDX>(csr.indptr, tcsr.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(csr.indices, tcsr.indices));
}
TEST(SpmatTest, COOToCSR) {
_TestCOOToCSR<int32_t>(CPU);
_TestCOOToCSR<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCOOToCSR<int32_t>(GPU);
_TestCOOToCSR<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCOOHasDuplicate() {
auto coo = COO1<IDX>();
ASSERT_FALSE(aten::COOHasDuplicate(coo));
coo = COO2<IDX>();
ASSERT_TRUE(aten::COOHasDuplicate(coo));
}
TEST(SpmatTest, TestCOOHasDuplicate) {
_TestCOOHasDuplicate<int32_t>();
_TestCOOHasDuplicate<int64_t>();
}
template <typename IDX>
void _TestCOOSort(DGLContext ctx) {
auto coo = COO3<IDX>(ctx);
auto sr_coo = COOSort(coo, false);
ASSERT_EQ(coo.num_rows, sr_coo.num_rows);
ASSERT_EQ(coo.num_cols, sr_coo.num_cols);
ASSERT_TRUE(sr_coo.row_sorted);
auto flags = COOIsSorted(sr_coo);
ASSERT_TRUE(flags.first);
flags = COOIsSorted(coo); // original coo should stay the same
ASSERT_FALSE(flags.first);
ASSERT_FALSE(flags.second);
auto src_coo = COOSort(coo, true);
ASSERT_EQ(coo.num_rows, src_coo.num_rows);
ASSERT_EQ(coo.num_cols, src_coo.num_cols);
ASSERT_TRUE(src_coo.row_sorted);
ASSERT_TRUE(src_coo.col_sorted);
flags = COOIsSorted(src_coo);
ASSERT_TRUE(flags.first);
ASSERT_TRUE(flags.second);
// sort inplace
COOSort_(&coo);
ASSERT_TRUE(coo.row_sorted);
flags = COOIsSorted(coo);
ASSERT_TRUE(flags.first);
COOSort_(&coo, true);
ASSERT_TRUE(coo.row_sorted);
ASSERT_TRUE(coo.col_sorted);
flags = COOIsSorted(coo);
ASSERT_TRUE(flags.first);
ASSERT_TRUE(flags.second);
// COO3
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 1, 2, 3, 4, 5]
// row : [0, 2, 0, 1, 2, 0]
// col : [2, 2, 1, 0, 3, 2]
// Row Sorted
// data: [0, 2, 5, 3, 1, 4]
// row : [0, 0, 0, 1, 2, 2]
// col : [2, 1, 2, 0, 2, 3]
// Row Col Sorted
// data: [2, 0, 5, 3, 1, 4]
// row : [0, 0, 0, 1, 2, 2]
// col : [1, 2, 2, 0, 2, 3]
auto sort_row = aten::VecToIdArray(
std::vector<IDX>({0, 0, 0, 1, 2, 2}), sizeof(IDX) * 8, ctx);
auto sort_col = aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx);
auto sort_col_data = aten::VecToIdArray(
std::vector<IDX>({2, 0, 5, 3, 1, 4}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(sr_coo.row, sort_row));
ASSERT_TRUE(ArrayEQ<IDX>(src_coo.row, sort_row));
ASSERT_TRUE(ArrayEQ<IDX>(src_coo.col, sort_col));
ASSERT_TRUE(ArrayEQ<IDX>(src_coo.data, sort_col_data));
}
TEST(SpmatTest, COOSort) {
_TestCOOSort<int32_t>(CPU);
_TestCOOSort<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCOOSort<int32_t>(GPU);
_TestCOOSort<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCOOReorder() {
auto coo = COO2<IDX>();
auto new_row =
aten::VecToIdArray(std::vector<IDX>({2, 0, 3, 1}), sizeof(IDX) * 8, CTX);
auto new_col = aten::VecToIdArray(
std::vector<IDX>({2, 0, 4, 3, 1}), sizeof(IDX) * 8, CTX);
auto new_coo = COOReorder(coo, new_row, new_col);
ASSERT_EQ(new_coo.num_rows, coo.num_rows);
ASSERT_EQ(new_coo.num_cols, coo.num_cols);
}
TEST(SpmatTest, TestCOOReorder) {
_TestCOOReorder<int32_t>();
_TestCOOReorder<int64_t>();
}
template <typename IDX>
void _TestCOOGetData(DGLContext ctx) {
auto coo = COO2<IDX>(ctx);
// test get all data
auto x = aten::COOGetAllData(coo, 0, 0);
auto tx = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
x = aten::COOGetAllData(coo, 0, 2);
tx = aten::VecToIdArray(std::vector<IDX>({2, 5}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
// test get data
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, ctx);
auto c =
aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
x = aten::COOGetData(coo, r, c);
tx = aten::VecToIdArray(std::vector<IDX>({-1, 0, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
// test get data on sorted
coo = aten::COOSort(coo);
r = aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, ctx);
c = aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
x = aten::COOGetData(coo, r, c);
tx = aten::VecToIdArray(std::vector<IDX>({-1, 0, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
// test get data w/ broadcasting
r = aten::VecToIdArray(std::vector<IDX>({0}), sizeof(IDX) * 8, ctx);
c = aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
x = aten::COOGetData(coo, r, c);
tx = aten::VecToIdArray(std::vector<IDX>({-1, 0, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
}
TEST(SpmatTest, COOGetData) {
_TestCOOGetData<int32_t>(CPU);
_TestCOOGetData<int64_t>(CPU);
// #ifdef DGL_USE_CUDA
//_TestCOOGetData<int32_t>(GPU);
//_TestCOOGetData<int64_t>(GPU);
// #endif
}
template <typename IDX>
void _TestCOOGetDataAndIndices() {
auto coo = COO2<IDX>();
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, CTX);
auto c =
aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, CTX);
auto x = aten::COOGetDataAndIndices(coo, r, c);
auto tr =
aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, CTX);
auto tc =
aten::VecToIdArray(std::vector<IDX>({1, 2, 2}), sizeof(IDX) * 8, CTX);
auto td =
aten::VecToIdArray(std::vector<IDX>({0, 2, 5}), sizeof(IDX) * 8, CTX);
ASSERT_TRUE(ArrayEQ<IDX>(x[0], tr));
ASSERT_TRUE(ArrayEQ<IDX>(x[1], tc));
ASSERT_TRUE(ArrayEQ<IDX>(x[2], td));
}
TEST(SpmatTest, COOGetDataAndIndices) {
_TestCOOGetDataAndIndices<int32_t>();
_TestCOOGetDataAndIndices<int64_t>();
}
template <typename IDX>
void _TestCOOToCSRAlgs() {
// Compare results between different CPU COOToCSR implementations.
// NNZ is chosen to be bigger than the limit for the "small" matrix algorithm.
// N is set to lay on border between "sparse" and "dense" algorithm choice.
const int64_t num_threads = std::min(256, omp_get_max_threads());
const int64_t min_num_threads = 3;
if (num_threads < min_num_threads) {
std::cerr << "[ ] [ INFO ]"
<< "This test requires at least 3 OMP threads to work properly"
<< std::endl;
GTEST_SKIP();
return;
}
// Select N and NNZ for COO matrix in a way than depending on number of
// threads different algorithm will be used.
// See WhichCOOToCSR in src/array/cpu/spmat_op_impl_coo.cc for details
const int64_t type_scale = sizeof(IDX) >> 1;
const int64_t small = 50 * num_threads * type_scale * type_scale;
// NNZ should be bigger than limit for small matrix algorithm
const int64_t nnz = small + 1234;
// N is chosen to lay on sparse/dense border
const int64_t n = type_scale * nnz / num_threads;
const IDX rows_nad_cols = n + 1; // should be bigger than sparse/dense border
// Note that it will be better to set the seed to a random value when gtest
// allows to use --gtest_random_seed without --gtest_shuffle and report this
// value for reproduction. This way we can find unforeseen situations and
// potential bugs.
const auto seed = 123321;
auto coo = COORandomized<IDX>(rows_nad_cols, nnz, seed);
omp_set_num_threads(1);
// UnSortedSmallCOOToCSR will be used
auto tcsr_small = aten::COOToCSR(coo);
ASSERT_EQ(coo.num_rows, tcsr_small.num_rows);
ASSERT_EQ(coo.num_cols, tcsr_small.num_cols);
omp_set_num_threads(num_threads - 1);
// UnSortedDenseCOOToCSR will be used
auto tcsr_dense = aten::COOToCSR(coo);
ASSERT_EQ(tcsr_small.num_rows, tcsr_dense.num_rows);
ASSERT_EQ(tcsr_small.num_cols, tcsr_dense.num_cols);
ASSERT_TRUE(ArrayEQ<IDX>(tcsr_small.indptr, tcsr_dense.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(tcsr_small.indices, tcsr_dense.indices));
ASSERT_TRUE(ArrayEQ<IDX>(tcsr_small.data, tcsr_dense.data));
omp_set_num_threads(num_threads);
// UnSortedSparseCOOToCSR will be used
auto tcsr_sparse = aten::COOToCSR(coo);
ASSERT_EQ(tcsr_small.num_rows, tcsr_sparse.num_rows);
ASSERT_EQ(tcsr_small.num_cols, tcsr_sparse.num_cols);
ASSERT_TRUE(ArrayEQ<IDX>(tcsr_small.indptr, tcsr_sparse.indptr));
ASSERT_TRUE(ArrayEQ<IDX>(tcsr_small.indices, tcsr_sparse.indices));
ASSERT_TRUE(ArrayEQ<IDX>(tcsr_small.data, tcsr_sparse.data));
return;
}
TEST(SpmatTest, COOToCSRAlgs) {
_TestCOOToCSRAlgs<int32_t>();
_TestCOOToCSRAlgs<int64_t>();
}
+760
View File
@@ -0,0 +1,760 @@
#include <dgl/array.h>
#include <gtest/gtest.h>
#include "./common.h"
using namespace dgl;
using namespace dgl::runtime;
namespace {
template <typename IDX>
aten::CSRMatrix CSR1(DGLContext ctx = CTX) {
// [[0, 1, 1, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 3, 1, 4]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 3, 5, 5}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 0, 3, 2}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 2, 3, 4, 1}), sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::CSRMatrix CSR2(DGLContext ctx = CTX) {
// has duplicate entries
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 5, 3, 1, 4]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 3, 4, 6, 6}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 2, 5, 3, 1, 4}), sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::CSRMatrix CSR3(DGLContext ctx = CTX) {
// has duplicate entries and the columns are not sorted
// [[0, 1, 1, 1, 0, 0],
// [1, 0, 0, 0, 0, 0],
// [0, 0, 1, 1, 0, 0],
// [0, 0, 0, 0, 0, 0],
// [1, 1, 1, 0, 0, 0],
// [0, 0, 0, 1, 0, 0],
// [0, 0, 0, 0, 0, 0],
// [1, 2, 1, 1, 0, 0],
// [0, 1, 0, 0, 0, 1]],
// data: [5, 2, 0, 3, 1, 4, 8, 7, 6, 9, 12, 13, 11, 10, 14, 15, 16]
return aten::CSRMatrix(
9, 6,
aten::VecToIdArray(
std::vector<IDX>({0, 3, 4, 6, 6, 9, 10, 10, 15, 17}), sizeof(IDX) * 8,
ctx),
aten::VecToIdArray(
std::vector<IDX>({3, 2, 1, 0, 2, 3, 1, 2, 0, 3, 1, 2, 1, 3, 0, 5, 1}),
sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>(
{0, 2, 5, 3, 1, 4, 6, 8, 7, 9, 13, 10, 11, 14, 12, 16, 15}),
sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::COOMatrix COO1(DGLContext ctx = CTX) {
// [[0, 1, 1, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 3, 1, 4]
// row : [0, 2, 0, 1, 2]
// col : [1, 2, 2, 0, 3]
return aten::COOMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 0, 1, 2}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 3, 1, 2, 4}), sizeof(IDX) * 8, ctx));
}
template <typename IDX>
aten::COOMatrix COO2(DGLContext ctx = CTX) {
// has duplicate entries
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 5, 3, 1, 4]
// row : [0, 2, 0, 1, 2, 0]
// col : [1, 2, 2, 0, 3, 2]
return aten::COOMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 0, 1, 2, 0}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 3, 2}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 1, 2, 3, 4, 5}), sizeof(IDX) * 8, ctx));
}
template <typename IDX>
aten::CSRMatrix SR_CSR3(DGLContext ctx) {
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 3, 4, 6, 6}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({2, 1, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({0, 2, 5, 3, 1, 4}), sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::CSRMatrix SRC_CSR3(DGLContext ctx) {
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
return aten::CSRMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 3, 4, 6, 6}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({1, 2, 2, 0, 2, 3}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({2, 0, 5, 3, 1, 4}), sizeof(IDX) * 8, ctx),
false);
}
template <typename IDX>
aten::COOMatrix COO3(DGLContext ctx) {
// has duplicate entries
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// row : [0, 2, 0, 1, 2, 0]
// col : [2, 2, 1, 0, 3, 2]
return aten::COOMatrix(
4, 5,
aten::VecToIdArray(
std::vector<IDX>({0, 2, 0, 1, 2, 0}), sizeof(IDX) * 8, ctx),
aten::VecToIdArray(
std::vector<IDX>({2, 2, 1, 0, 3, 2}), sizeof(IDX) * 8, ctx));
}
} // namespace
template <typename IDX>
void _TestCSRIsNonZero1(DGLContext ctx) {
auto csr = CSR1<IDX>(ctx);
ASSERT_TRUE(aten::CSRIsNonZero(csr, 0, 1));
ASSERT_FALSE(aten::CSRIsNonZero(csr, 0, 0));
IdArray r =
aten::VecToIdArray(std::vector<IDX>({2, 2, 0, 0}), sizeof(IDX) * 8, ctx);
IdArray c =
aten::VecToIdArray(std::vector<IDX>({1, 1, 1, 3}), sizeof(IDX) * 8, ctx);
IdArray x = aten::CSRIsNonZero(csr, r, c);
IdArray tx =
aten::VecToIdArray(std::vector<IDX>({0, 0, 1, 0}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
}
template <typename IDX>
void _TestCSRIsNonZero2(DGLContext ctx) {
auto csr = CSR3<IDX>(ctx);
ASSERT_TRUE(aten::CSRIsNonZero(csr, 0, 1));
ASSERT_FALSE(aten::CSRIsNonZero(csr, 0, 0));
IdArray r = aten::VecToIdArray(
std::vector<IDX>({
0,
0,
0,
0,
0,
}),
sizeof(IDX) * 8, ctx);
IdArray c = aten::VecToIdArray(
std::vector<IDX>({
0,
1,
2,
3,
4,
}),
sizeof(IDX) * 8, ctx);
IdArray x = aten::CSRIsNonZero(csr, r, c);
IdArray tx = aten::VecToIdArray(
std::vector<IDX>({0, 1, 1, 1, 0}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx)) << " x = " << x << ", tx = " << tx;
}
TEST(SpmatTest, TestCSRIsNonZero) {
_TestCSRIsNonZero1<int32_t>(CPU);
_TestCSRIsNonZero1<int64_t>(CPU);
_TestCSRIsNonZero2<int32_t>(CPU);
_TestCSRIsNonZero2<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRIsNonZero1<int32_t>(GPU);
_TestCSRIsNonZero1<int64_t>(GPU);
_TestCSRIsNonZero2<int32_t>(GPU);
_TestCSRIsNonZero2<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRGetRowNNZ(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
ASSERT_EQ(aten::CSRGetRowNNZ(csr, 0), 3);
ASSERT_EQ(aten::CSRGetRowNNZ(csr, 3), 0);
IdArray r =
aten::VecToIdArray(std::vector<IDX>({0, 3}), sizeof(IDX) * 8, ctx);
IdArray x = aten::CSRGetRowNNZ(csr, r);
IdArray tx =
aten::VecToIdArray(std::vector<IDX>({3, 0}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
}
TEST(SpmatTest, TestCSRGetRowNNZ) {
_TestCSRGetRowNNZ<int32_t>(CPU);
_TestCSRGetRowNNZ<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRGetRowNNZ<int32_t>(GPU);
_TestCSRGetRowNNZ<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRGetRowColumnIndices(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
auto x = aten::CSRGetRowColumnIndices(csr, 0);
auto tx =
aten::VecToIdArray(std::vector<IDX>({1, 2, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
x = aten::CSRGetRowColumnIndices(csr, 1);
tx = aten::VecToIdArray(std::vector<IDX>({0}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
x = aten::CSRGetRowColumnIndices(csr, 3);
tx = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
}
TEST(SpmatTest, TestCSRGetRowColumnIndices) {
_TestCSRGetRowColumnIndices<int32_t>(CPU);
_TestCSRGetRowColumnIndices<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRGetRowColumnIndices<int32_t>(GPU);
_TestCSRGetRowColumnIndices<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRGetRowData(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
auto x = aten::CSRGetRowData(csr, 0);
auto tx =
aten::VecToIdArray(std::vector<IDX>({0, 2, 5}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
x = aten::CSRGetRowData(csr, 1);
tx = aten::VecToIdArray(std::vector<IDX>({3}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
x = aten::CSRGetRowData(csr, 3);
tx = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
}
TEST(SpmatTest, TestCSRGetRowData) {
_TestCSRGetRowData<int32_t>(CPU);
_TestCSRGetRowData<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRGetRowData<int32_t>(GPU);
_TestCSRGetRowData<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRGetData(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
// test get all data
auto x = aten::CSRGetAllData(csr, 0, 0);
auto tx = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
x = aten::CSRGetAllData(csr, 0, 2);
tx = aten::VecToIdArray(std::vector<IDX>({2, 5}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
// test get data
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, ctx);
auto c =
aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
x = aten::CSRGetData(csr, r, c);
tx = aten::VecToIdArray(std::vector<IDX>({-1, 0, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
// test get data on sorted
csr = aten::CSRSort(csr);
r = aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, ctx);
c = aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
x = aten::CSRGetData(csr, r, c);
tx = aten::VecToIdArray(std::vector<IDX>({-1, 0, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
// test get data w/ broadcasting
r = aten::VecToIdArray(std::vector<IDX>({0}), sizeof(IDX) * 8, ctx);
c = aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
x = aten::CSRGetData(csr, r, c);
tx = aten::VecToIdArray(std::vector<IDX>({-1, 0, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x, tx));
}
TEST(SpmatTest, CSRGetData) {
_TestCSRGetData<int32_t>(CPU);
_TestCSRGetData<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRGetData<int32_t>(GPU);
_TestCSRGetData<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRGetDataAndIndices(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, ctx);
auto c =
aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
auto x = aten::CSRGetDataAndIndices(csr, r, c);
auto tr =
aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, ctx);
auto tc =
aten::VecToIdArray(std::vector<IDX>({1, 2, 2}), sizeof(IDX) * 8, ctx);
auto td =
aten::VecToIdArray(std::vector<IDX>({0, 2, 5}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x[0], tr));
ASSERT_TRUE(ArrayEQ<IDX>(x[1], tc));
ASSERT_TRUE(ArrayEQ<IDX>(x[2], td));
}
TEST(SpmatTest, CSRGetDataAndIndices) {
_TestCSRGetDataAndIndices<int32_t>(CPU);
_TestCSRGetDataAndIndices<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRGetDataAndIndices<int32_t>(GPU);
_TestCSRGetDataAndIndices<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRTranspose(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
auto csr_t = aten::CSRTranspose(csr);
// [[0, 1, 0, 0],
// [1, 0, 0, 0],
// [2, 0, 1, 0],
// [0, 0, 1, 0],
// [0, 0, 0, 0]]
// data: [3, 0, 2, 5, 1, 4]
ASSERT_EQ(csr_t.num_rows, 5);
ASSERT_EQ(csr_t.num_cols, 4);
auto tp = aten::VecToIdArray(
std::vector<IDX>({0, 1, 2, 5, 6, 6}), sizeof(IDX) * 8, ctx);
auto ti = aten::VecToIdArray(
std::vector<IDX>({1, 0, 0, 0, 2, 2}), sizeof(IDX) * 8, ctx);
auto td = aten::VecToIdArray(
std::vector<IDX>({3, 0, 2, 5, 1, 4}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(csr_t.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(csr_t.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(csr_t.data, td));
}
TEST(SpmatTest, CSRTranspose) {
_TestCSRTranspose<int32_t>(CPU);
_TestCSRTranspose<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRTranspose<int32_t>(GPU);
_TestCSRTranspose<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRToCOO(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
{
auto coo = CSRToCOO(csr, false);
ASSERT_EQ(coo.num_rows, 4);
ASSERT_EQ(coo.num_cols, 5);
ASSERT_TRUE(coo.row_sorted);
auto tr = aten::VecToIdArray(
std::vector<IDX>({0, 0, 0, 1, 2, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(coo.row, tr));
ASSERT_TRUE(ArrayEQ<IDX>(coo.col, csr.indices));
ASSERT_TRUE(ArrayEQ<IDX>(coo.data, csr.data));
// convert from sorted csr
auto s_csr = CSRSort(csr);
coo = CSRToCOO(s_csr, false);
ASSERT_EQ(coo.num_rows, 4);
ASSERT_EQ(coo.num_cols, 5);
ASSERT_TRUE(coo.row_sorted);
ASSERT_TRUE(coo.col_sorted);
tr = aten::VecToIdArray(
std::vector<IDX>({0, 0, 0, 1, 2, 2}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(coo.row, tr));
ASSERT_TRUE(ArrayEQ<IDX>(coo.col, s_csr.indices));
ASSERT_TRUE(ArrayEQ<IDX>(coo.data, s_csr.data));
}
{
auto coo = CSRToCOO(csr, true);
ASSERT_EQ(coo.num_rows, 4);
ASSERT_EQ(coo.num_cols, 5);
auto tcoo = COO2<IDX>(ctx);
ASSERT_TRUE(ArrayEQ<IDX>(coo.row, tcoo.row));
ASSERT_TRUE(ArrayEQ<IDX>(coo.col, tcoo.col));
}
}
TEST(SpmatTest, CSRToCOO) {
_TestCSRToCOO<int32_t>(CPU);
_TestCSRToCOO<int64_t>(CPU);
#if DGL_USE_CUDA
_TestCSRToCOO<int32_t>(GPU);
_TestCSRToCOO<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRSliceRows(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
auto x = aten::CSRSliceRows(csr, 1, 4);
// [1, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 0, 0, 0]]
// data: [3, 1, 4]
ASSERT_EQ(x.num_rows, 3);
ASSERT_EQ(x.num_cols, 5);
auto tp =
aten::VecToIdArray(std::vector<IDX>({0, 1, 3, 3}), sizeof(IDX) * 8, ctx);
auto ti =
aten::VecToIdArray(std::vector<IDX>({0, 2, 3}), sizeof(IDX) * 8, ctx);
auto td =
aten::VecToIdArray(std::vector<IDX>({3, 1, 4}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 1, 3}), sizeof(IDX) * 8, ctx);
x = aten::CSRSliceRows(csr, r);
// [[0, 1, 2, 0, 0],
// [1, 0, 0, 0, 0],
// [0, 0, 0, 0, 0]]
// data: [0, 2, 5, 3]
tp = aten::VecToIdArray(std::vector<IDX>({0, 3, 4, 4}), sizeof(IDX) * 8, ctx);
ti = aten::VecToIdArray(std::vector<IDX>({1, 2, 2, 0}), sizeof(IDX) * 8, ctx);
td = aten::VecToIdArray(std::vector<IDX>({0, 2, 5, 3}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
// Testing non-increasing row id based slicing
r = aten::VecToIdArray(std::vector<IDX>({3, 2, 1}), sizeof(IDX) * 8, ctx);
x = aten::CSRSliceRows(csr, r);
// [[0, 0, 0, 0, 0],
// [0, 0, 1, 1, 0],
// [1, 0, 0, 0, 0]]
// data: [1, 4, 3]
tp = aten::VecToIdArray(std::vector<IDX>({0, 0, 2, 3}), sizeof(IDX) * 8, ctx);
ti = aten::VecToIdArray(std::vector<IDX>({2, 3, 0}), sizeof(IDX) * 8, ctx);
td = aten::VecToIdArray(std::vector<IDX>({1, 4, 3}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
// Testing zero-degree row slicing with different rows
r = aten::VecToIdArray(
std::vector<IDX>({1, 3, 0, 3, 2}), sizeof(IDX) * 8, ctx);
x = aten::CSRSliceRows(csr, r);
// [[1, 0, 0, 0, 0],
// [0, 0, 0, 0, 0],
// [0, 1, 2, 0, 0],
// [0, 0, 0, 0, 0],
// [0, 0, 1, 1, 0]]
// data: [3, 0, 2, 5, 1, 4]
tp = aten::VecToIdArray(
std::vector<IDX>({0, 1, 1, 4, 4, 6}), sizeof(IDX) * 8, ctx);
ti = aten::VecToIdArray(
std::vector<IDX>({0, 1, 2, 2, 2, 3}), sizeof(IDX) * 8, ctx);
td = aten::VecToIdArray(
std::vector<IDX>({3, 0, 2, 5, 1, 4}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
// Testing empty output (i.e. sliced rows will be zero-degree)
r = aten::VecToIdArray(std::vector<IDX>({3, 3, 3}), sizeof(IDX) * 8, ctx);
x = aten::CSRSliceRows(csr, r);
// [[0, 0, 0, 0, 0],
// [0, 0, 0, 0, 0],
// [0, 0, 0, 0, 0]]
// data: []
tp = aten::VecToIdArray(std::vector<IDX>({0, 0, 0, 0}), sizeof(IDX) * 8, ctx);
ti = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
td = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
// Testing constant output: we pick last row with at least one nnz
r = aten::VecToIdArray(std::vector<IDX>({2, 2, 2}), sizeof(IDX) * 8, ctx);
x = aten::CSRSliceRows(csr, r);
// [[0, 0, 1, 1, 0],
// [0, 0, 1, 1, 0],
// [0, 0, 1, 1, 0]]
// data: [1, 4, 1, 4, 1, 4]
tp = aten::VecToIdArray(std::vector<IDX>({0, 2, 4, 6}), sizeof(IDX) * 8, ctx);
ti = aten::VecToIdArray(
std::vector<IDX>({2, 3, 2, 3, 2, 3}), sizeof(IDX) * 8, ctx);
td = aten::VecToIdArray(
std::vector<IDX>({1, 4, 1, 4, 1, 4}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
}
TEST(SpmatTest, TestCSRSliceRows) {
_TestCSRSliceRows<int32_t>(CPU);
_TestCSRSliceRows<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRSliceRows<int32_t>(GPU);
_TestCSRSliceRows<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRSliceMatrix1(DGLContext ctx) {
auto csr = CSR2<IDX>(ctx);
{
// square
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 1, 3}), sizeof(IDX) * 8, ctx);
auto c =
aten::VecToIdArray(std::vector<IDX>({1, 2, 3}), sizeof(IDX) * 8, ctx);
auto x = aten::CSRSliceMatrix(csr, r, c);
// [[1, 2, 0],
// [0, 0, 0],
// [0, 0, 0]]
// data: [0, 2, 5]
ASSERT_EQ(x.num_rows, 3);
ASSERT_EQ(x.num_cols, 3);
auto tp = aten::VecToIdArray(
std::vector<IDX>({0, 3, 3, 3}), sizeof(IDX) * 8, ctx);
auto ti =
aten::VecToIdArray(std::vector<IDX>({0, 1, 1}), sizeof(IDX) * 8, ctx);
auto td =
aten::VecToIdArray(std::vector<IDX>({0, 2, 5}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
}
{
// non-square
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
auto c = aten::VecToIdArray(std::vector<IDX>({0, 1}), sizeof(IDX) * 8, ctx);
auto x = aten::CSRSliceMatrix(csr, r, c);
// [[0, 1],
// [1, 0],
// [0, 0]]
// data: [0, 3]
ASSERT_EQ(x.num_rows, 3);
ASSERT_EQ(x.num_cols, 2);
auto tp = aten::VecToIdArray(
std::vector<IDX>({0, 1, 2, 2}), sizeof(IDX) * 8, ctx);
auto ti =
aten::VecToIdArray(std::vector<IDX>({1, 0}), sizeof(IDX) * 8, ctx);
auto td =
aten::VecToIdArray(std::vector<IDX>({0, 3}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
}
{
// empty slice
auto r = aten::VecToIdArray(std::vector<IDX>({2, 3}), sizeof(IDX) * 8, ctx);
auto c = aten::VecToIdArray(std::vector<IDX>({0, 1}), sizeof(IDX) * 8, ctx);
auto x = aten::CSRSliceMatrix(csr, r, c);
// [[0, 0],
// [0, 0]]
// data: []
ASSERT_EQ(x.num_rows, 2);
ASSERT_EQ(x.num_cols, 2);
auto tp =
aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, ctx);
auto ti = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
auto td = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
}
}
template <typename IDX>
void _TestCSRSliceMatrix2(DGLContext ctx) {
auto csr = CSR3<IDX>(ctx);
{
// square
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 1, 3}), sizeof(IDX) * 8, ctx);
auto c =
aten::VecToIdArray(std::vector<IDX>({1, 2, 3}), sizeof(IDX) * 8, ctx);
auto x = aten::CSRSliceMatrix(csr, r, c);
// [[1, 1, 1],
// [0, 0, 0],
// [0, 0, 0]]
// data: [5, 2, 0]
ASSERT_EQ(x.num_rows, 3);
ASSERT_EQ(x.num_cols, 3);
auto tp = aten::VecToIdArray(
std::vector<IDX>({0, 3, 3, 3}), sizeof(IDX) * 8, ctx);
// indexes are in reverse order in CSR3
auto ti =
aten::VecToIdArray(std::vector<IDX>({2, 1, 0}), sizeof(IDX) * 8, ctx);
auto td =
aten::VecToIdArray(std::vector<IDX>({0, 2, 5}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
}
{
// non-square
auto r =
aten::VecToIdArray(std::vector<IDX>({0, 1, 2}), sizeof(IDX) * 8, ctx);
auto c = aten::VecToIdArray(std::vector<IDX>({0, 1}), sizeof(IDX) * 8, ctx);
auto x = aten::CSRSliceMatrix(csr, r, c);
// [[0, 1],
// [1, 0],
// [0, 0]]
// data: [0, 3]
ASSERT_EQ(x.num_rows, 3);
ASSERT_EQ(x.num_cols, 2);
auto tp = aten::VecToIdArray(
std::vector<IDX>({0, 1, 2, 2}), sizeof(IDX) * 8, ctx);
auto ti =
aten::VecToIdArray(std::vector<IDX>({1, 0}), sizeof(IDX) * 8, ctx);
auto td =
aten::VecToIdArray(std::vector<IDX>({5, 3}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
}
{
// empty slice
auto r = aten::VecToIdArray(std::vector<IDX>({2, 3}), sizeof(IDX) * 8, ctx);
auto c = aten::VecToIdArray(std::vector<IDX>({0, 1}), sizeof(IDX) * 8, ctx);
auto x = aten::CSRSliceMatrix(csr, r, c);
// [[0, 0],
// [0, 0]]
// data: []
ASSERT_EQ(x.num_rows, 2);
ASSERT_EQ(x.num_cols, 2);
auto tp =
aten::VecToIdArray(std::vector<IDX>({0, 0, 0}), sizeof(IDX) * 8, ctx);
auto ti = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
auto td = aten::VecToIdArray(std::vector<IDX>({}), sizeof(IDX) * 8, ctx);
ASSERT_TRUE(ArrayEQ<IDX>(x.indptr, tp));
ASSERT_TRUE(ArrayEQ<IDX>(x.indices, ti));
ASSERT_TRUE(ArrayEQ<IDX>(x.data, td));
}
}
TEST(SpmatTest, CSRSliceMatrix) {
_TestCSRSliceMatrix1<int32_t>(CPU);
_TestCSRSliceMatrix1<int64_t>(CPU);
_TestCSRSliceMatrix2<int32_t>(CPU);
_TestCSRSliceMatrix2<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRSliceMatrix1<int32_t>(GPU);
_TestCSRSliceMatrix1<int64_t>(GPU);
_TestCSRSliceMatrix2<int32_t>(GPU);
_TestCSRSliceMatrix2<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRHasDuplicate(DGLContext ctx) {
auto csr = CSR1<IDX>(ctx);
ASSERT_FALSE(aten::CSRHasDuplicate(csr));
csr = CSR2<IDX>(ctx);
ASSERT_TRUE(aten::CSRHasDuplicate(csr));
}
TEST(SpmatTest, CSRHasDuplicate) {
_TestCSRHasDuplicate<int32_t>(CPU);
_TestCSRHasDuplicate<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRHasDuplicate<int32_t>(GPU);
_TestCSRHasDuplicate<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRSort(DGLContext ctx) {
auto csr = CSR1<IDX>(ctx);
ASSERT_FALSE(aten::CSRIsSorted(csr));
auto csr1 = aten::CSRSort(csr);
ASSERT_FALSE(aten::CSRIsSorted(csr));
ASSERT_TRUE(aten::CSRIsSorted(csr1));
ASSERT_TRUE(csr1.sorted);
aten::CSRSort_(&csr);
ASSERT_TRUE(aten::CSRIsSorted(csr));
ASSERT_TRUE(csr.sorted);
csr = CSR2<IDX>(ctx);
ASSERT_TRUE(aten::CSRIsSorted(csr));
}
TEST(SpmatTest, CSRSort) {
_TestCSRSort<int32_t>(CPU);
_TestCSRSort<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestCSRSort<int32_t>(GPU);
_TestCSRSort<int64_t>(GPU);
#endif
}
template <typename IDX>
void _TestCSRReorder() {
auto csr = CSR2<IDX>();
auto new_row =
aten::VecToIdArray(std::vector<IDX>({2, 0, 3, 1}), sizeof(IDX) * 8, CTX);
auto new_col = aten::VecToIdArray(
std::vector<IDX>({2, 0, 4, 3, 1}), sizeof(IDX) * 8, CTX);
auto new_csr = CSRReorder(csr, new_row, new_col);
ASSERT_EQ(new_csr.num_rows, csr.num_rows);
ASSERT_EQ(new_csr.num_cols, csr.num_cols);
}
TEST(SpmatTest, TestCSRReorder) {
_TestCSRReorder<int32_t>();
_TestCSRReorder<int64_t>();
}
+244
View File
@@ -0,0 +1,244 @@
#if !defined(_WIN32)
#include <../src/array/cpu/spmm.h>
#include <dgl/array.h>
#include <gtest/gtest.h>
#include <time.h>
#include <random>
#include "./common.h"
using namespace dgl;
using namespace dgl::runtime;
int sizes[] = {1, 7, 8, 9, 31, 32, 33, 54, 63, 64, 65, 256, 257};
namespace ns_op = dgl::aten::cpu::op;
namespace {
template <class T>
void GenerateData(T* data, int dim, T mul) {
for (int i = 0; i < dim; i++) {
data[i] = (i + 1) * mul;
}
}
template <class T>
void GenerateRandomData(T* data, int dim) {
std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<> dist(0, 10000);
for (int i = 0; i < dim; i++) {
data[i] = (dist(rng) / 100);
}
}
template <class T>
void GenerateZeroData(T* data, int dim) {
for (int i = 0; i < dim; i++) {
data[i] = 0;
}
}
template <class T>
void Copy(T* exp, T* out, T* hs, int dim) {
for (int i = 0; i < dim; i++) {
exp[i] = out[i] + hs[i];
}
}
template <class T>
void Add(T* exp, T* out, T* lhs, T* rhs, int dim) {
for (int i = 0; i < dim; i++) {
exp[i] = out[i] + lhs[i] + rhs[i];
}
}
template <class T>
void Sub(T* exp, T* out, T* lhs, T* rhs, int dim) {
for (int i = 0; i < dim; i++) {
exp[i] = out[i] + lhs[i] - rhs[i];
}
}
template <class T>
void Mul(T* exp, T* out, T* lhs, T* rhs, int dim) {
for (int i = 0; i < dim; i++) {
exp[i] = (out[i] + (lhs[i] * rhs[i]));
}
}
template <class T>
void Div(T* exp, T* out, T* lhs, T* rhs, int dim) {
for (int i = 0; i < dim; i++) {
exp[i] = (out[i] + (lhs[i] / rhs[i]));
}
}
template <class T>
void CheckResult(T* exp, T* out, int dim) {
for (int i = 0; i < dim; i++) {
ASSERT_TRUE(exp[i] == out[i]);
}
}
} // namespace
template <typename IDX>
void _TestSpmmCopyLhs() {
for (size_t i = 0; i < sizeof(sizes) / sizeof(int); i++) {
int dim = sizes[i];
IDX out[dim], exp[dim], lhs[dim];
GenerateZeroData(out, dim);
GenerateRandomData(lhs, dim);
// Calculation of expected output - 'exp'
Copy(exp, out, lhs, dim);
// Calculation of output using legacy path - 'out'
for (int k = 0; k < dim; k++) {
out[k] += ns_op::CopyLhs<IDX>::Call(lhs + k, nullptr);
}
CheckResult(exp, out, dim);
}
}
TEST(SpmmTest, TestSpmmCopyLhs) {
_TestSpmmCopyLhs<float>();
_TestSpmmCopyLhs<double>();
_TestSpmmCopyLhs<BFloat16>();
}
template <typename IDX>
void _TestSpmmCopyRhs() {
for (size_t i = 0; i < sizeof(sizes) / sizeof(int); i++) {
int dim = sizes[i];
IDX out[dim], exp[dim], rhs[dim];
GenerateZeroData(out, dim);
GenerateRandomData(rhs, dim);
// Calculation of expected output - 'exp'
Copy(exp, out, rhs, dim);
// Calculation of output using legacy path - 'out'
for (int k = 0; k < dim; k++) {
out[k] += ns_op::CopyRhs<IDX>::Call(nullptr, rhs + k);
}
CheckResult(exp, out, dim);
}
}
TEST(SpmmTest, TestSpmmCopyRhs) {
_TestSpmmCopyRhs<float>();
_TestSpmmCopyRhs<double>();
_TestSpmmCopyRhs<BFloat16>();
}
template <typename IDX>
void _TestSpmmAdd() {
for (size_t i = 0; i < sizeof(sizes) / sizeof(int); i++) {
int dim = sizes[i];
IDX out[dim], exp[dim], lhs[dim], rhs[dim];
GenerateZeroData(out, dim);
GenerateRandomData(lhs, dim);
GenerateRandomData(rhs, dim);
// Calculation of expected output - 'exp'
Add(exp, out, lhs, rhs, dim);
// Calculation of output using legacy path - 'out'
for (int k = 0; k < dim; k++) {
out[k] += ns_op::Add<IDX>::Call(lhs + k, rhs + k);
}
CheckResult(exp, out, dim);
}
}
TEST(SpmmTest, TestSpmmAdd) {
_TestSpmmAdd<float>();
_TestSpmmAdd<double>();
_TestSpmmAdd<BFloat16>();
}
template <typename IDX>
void _TestSpmmSub() {
for (size_t i = 0; i < sizeof(sizes) / sizeof(int); i++) {
int dim = sizes[i];
IDX out[dim], exp[dim], lhs[dim], rhs[dim];
GenerateZeroData(out, dim);
GenerateRandomData(lhs, dim);
GenerateRandomData(rhs, dim);
// Calculation of expected output - 'exp'
Sub(exp, out, lhs, rhs, dim);
// Calculation of output using legacy path - 'out'
for (int k = 0; k < dim; k++) {
out[k] += ns_op::Sub<IDX>::Call(lhs + k, rhs + k);
}
CheckResult(exp, out, dim);
}
}
TEST(SpmmTest, TestSpmmSub) {
_TestSpmmSub<float>();
_TestSpmmSub<double>();
_TestSpmmSub<BFloat16>();
}
template <typename IDX>
void _TestSpmmMul() {
for (size_t i = 0; i < sizeof(sizes) / sizeof(int); i++) {
int dim = sizes[i];
IDX out[dim], exp[dim], lhs[dim], rhs[dim];
GenerateZeroData(out, dim);
GenerateRandomData(lhs, dim);
GenerateRandomData(rhs, dim);
// Calculation of expected output - 'exp'
Mul(exp, out, lhs, rhs, dim);
// Calculation of output using legacy path - 'out'
for (int k = 0; k < dim; k++) {
out[k] += ns_op::Mul<IDX>::Call(lhs + k, rhs + k);
}
CheckResult(exp, out, dim);
}
}
TEST(SpmmTest, TestSpmmMul) {
_TestSpmmMul<float>();
_TestSpmmMul<double>();
_TestSpmmMul<BFloat16>();
}
template <typename IDX>
void _TestSpmmDiv() {
for (size_t i = 0; i < sizeof(sizes) / sizeof(int); i++) {
int dim = sizes[i];
IDX out[dim], exp[dim], lhs[dim], rhs[dim];
GenerateZeroData(out, dim);
GenerateData(lhs, dim, (IDX)15);
GenerateData(rhs, dim, (IDX)1);
// Calculation of expected output - 'exp'
Div(exp, out, lhs, rhs, dim);
// Calculation of output using legacy path - 'out'
for (int k = 0; k < dim; k++) {
out[k] += ns_op::Div<IDX>::Call(lhs + k, rhs + k);
}
CheckResult(exp, out, dim);
}
}
TEST(SpmmTest, TestSpmmDiv) {
_TestSpmmDiv<float>();
_TestSpmmDiv<double>();
_TestSpmmDiv<BFloat16>();
}
#endif // _WIN32
+434
View File
@@ -0,0 +1,434 @@
/**
* Copyright (c) 2019 by Contributors
* @file test_unit_graph.cc
* @brief Test UnitGraph
*/
#include <dgl/array.h>
#include <dgl/immutable_graph.h>
#include <dgl/runtime/device_api.h>
#include <gtest/gtest.h>
#include <memory>
#include <vector>
#include "../../src/graph/unit_graph.h"
#include "./../src/graph/heterograph.h"
#include "./common.h"
using namespace dgl;
using namespace dgl::runtime;
template <typename IdType>
aten::CSRMatrix CSR1(DGLContext ctx) {
/**
* G = [[0, 0, 1],
* [1, 0, 1],
* [0, 1, 0],
* [1, 0, 1]]
*/
IdArray g_indptr = aten::VecToIdArray(
std::vector<IdType>({0, 1, 3, 4, 6}), sizeof(IdType) * 8, CTX);
IdArray g_indices = aten::VecToIdArray(
std::vector<IdType>({2, 0, 2, 1, 0, 2}), sizeof(IdType) * 8, CTX);
const aten::CSRMatrix &csr_a =
aten::CSRMatrix(4, 3, g_indptr, g_indices, aten::NullArray(), false);
return csr_a;
}
template aten::CSRMatrix CSR1<int32_t>(DGLContext ctx);
template aten::CSRMatrix CSR1<int64_t>(DGLContext ctx);
template <typename IdType>
aten::COOMatrix COO1(DGLContext ctx) {
/**
* G = [[1, 1, 0],
* [0, 1, 0]]
*/
IdArray g_row = aten::VecToIdArray(
std::vector<IdType>({0, 0, 1}), sizeof(IdType) * 8, CTX);
IdArray g_col = aten::VecToIdArray(
std::vector<IdType>({0, 1, 1}), sizeof(IdType) * 8, CTX);
const aten::COOMatrix &coo =
aten::COOMatrix(2, 3, g_row, g_col, aten::NullArray(), true, true);
return coo;
}
template aten::COOMatrix COO1<int32_t>(DGLContext ctx);
template aten::COOMatrix COO1<int64_t>(DGLContext ctx);
template <typename IdType>
void _TestUnitGraph_InOutDegrees(DGLContext ctx) {
/**
InDegree(s) is available only if COO or CSC formats permitted.
OutDegree(s) is available only if COO or CSR formats permitted.
*/
// COO
{
const aten::COOMatrix &coo = COO1<IdType>(ctx);
auto &&g = CreateFromCOO(2, coo, COO_CODE);
ASSERT_EQ(g->InDegree(0, 0), 1);
auto &&nids = aten::Range(0, g->NumVertices(0), g->NumBits(), g->Context());
ASSERT_TRUE(ArrayEQ<IdType>(
g->InDegrees(0, nids),
aten::VecToIdArray<IdType>({1, 2}, g->NumBits(), g->Context())));
ASSERT_EQ(g->OutDegree(0, 0), 2);
ASSERT_TRUE(ArrayEQ<IdType>(
g->OutDegrees(0, nids),
aten::VecToIdArray<IdType>({2, 1}, g->NumBits(), g->Context())));
}
// CSC
{
const aten::CSRMatrix &csr = CSR1<IdType>(ctx);
auto &&g = CreateFromCSC(2, csr, CSC_CODE);
ASSERT_EQ(g->InDegree(0, 0), 1);
auto &&nids = aten::Range(0, g->NumVertices(0), g->NumBits(), g->Context());
ASSERT_TRUE(ArrayEQ<IdType>(
g->InDegrees(0, nids),
aten::VecToIdArray<IdType>({1, 2, 1}, g->NumBits(), g->Context())));
EXPECT_ANY_THROW(g->OutDegree(0, 0));
EXPECT_ANY_THROW(g->OutDegrees(0, nids));
}
// CSR
{
const aten::CSRMatrix &csr = CSR1<IdType>(ctx);
auto &&g = CreateFromCSR(2, csr, CSR_CODE);
ASSERT_EQ(g->OutDegree(0, 0), 1);
auto &&nids = aten::Range(0, g->NumVertices(0), g->NumBits(), g->Context());
ASSERT_TRUE(ArrayEQ<IdType>(
g->OutDegrees(0, nids),
aten::VecToIdArray<IdType>({1, 2, 1, 2}, g->NumBits(), g->Context())));
EXPECT_ANY_THROW(g->InDegree(0, 0));
EXPECT_ANY_THROW(g->InDegrees(0, nids));
}
}
template <typename IdType>
void _TestUnitGraph(DGLContext ctx) {
const aten::CSRMatrix &csr = CSR1<IdType>(ctx);
const aten::COOMatrix &coo = COO1<IdType>(ctx);
auto g = CreateFromCSC(2, csr);
ASSERT_EQ(g->GetCreatedFormats(), 4);
g = CreateFromCSR(2, csr);
ASSERT_EQ(g->GetCreatedFormats(), 2);
g = CreateFromCOO(2, coo);
ASSERT_EQ(g->GetCreatedFormats(), 1);
auto src = aten::VecToIdArray<int64_t>({1, 2, 5, 3});
auto dst = aten::VecToIdArray<int64_t>({1, 6, 2, 6});
auto mg = dgl::UnitGraph::CreateFromCOO(2, 9, 8, src, dst, COO_CODE);
ASSERT_EQ(mg->GetCreatedFormats(), 1);
auto hmg = dgl::UnitGraph::CreateFromCOO(1, 8, 8, src, dst, COO_CODE);
auto img = std::dynamic_pointer_cast<ImmutableGraph>(hmg->AsImmutableGraph());
ASSERT_TRUE(img != nullptr);
mg = dgl::UnitGraph::CreateFromCOO(2, 9, 8, src, dst, CSR_CODE | COO_CODE);
ASSERT_EQ(mg->GetCreatedFormats(), 1);
hmg = dgl::UnitGraph::CreateFromCOO(1, 8, 8, src, dst, CSR_CODE | COO_CODE);
img = std::dynamic_pointer_cast<ImmutableGraph>(hmg->AsImmutableGraph());
ASSERT_TRUE(img != nullptr);
mg = dgl::UnitGraph::CreateFromCOO(2, 9, 8, src, dst, CSC_CODE | COO_CODE);
ASSERT_EQ(mg->GetCreatedFormats(), 1);
hmg = dgl::UnitGraph::CreateFromCOO(1, 8, 8, src, dst, CSC_CODE | COO_CODE);
img = std::dynamic_pointer_cast<ImmutableGraph>(hmg->AsImmutableGraph());
ASSERT_TRUE(img != nullptr);
g = CreateFromCSC(2, csr);
ASSERT_EQ(g->GetCreatedFormats(), 4);
g = CreateFromCSR(2, csr);
ASSERT_EQ(g->GetCreatedFormats(), 2);
g = CreateFromCOO(2, coo);
ASSERT_EQ(g->GetCreatedFormats(), 1);
}
template <typename IdType>
void _TestUnitGraph_GetInCSR(DGLContext ctx) {
const aten::CSRMatrix &csr = CSR1<IdType>(ctx);
const aten::COOMatrix &coo = COO1<IdType>(ctx);
auto g = CreateFromCSC(2, csr);
auto in_csr_matrix = g->GetCSCMatrix(0);
ASSERT_EQ(in_csr_matrix.num_rows, csr.num_rows);
ASSERT_EQ(in_csr_matrix.num_cols, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 4);
// test out csr
g = CreateFromCSR(2, csr);
auto g_ptr = g->GetGraphInFormat(CSC_CODE);
in_csr_matrix = g_ptr->GetCSCMatrix(0);
ASSERT_EQ(in_csr_matrix.num_cols, csr.num_rows);
ASSERT_EQ(in_csr_matrix.num_rows, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 2);
in_csr_matrix = g->GetCSCMatrix(0);
ASSERT_EQ(in_csr_matrix.num_cols, csr.num_rows);
ASSERT_EQ(in_csr_matrix.num_rows, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 6);
// test out coo
g = CreateFromCOO(2, coo);
g_ptr = g->GetGraphInFormat(CSC_CODE);
in_csr_matrix = g_ptr->GetCSCMatrix(0);
ASSERT_EQ(in_csr_matrix.num_cols, coo.num_rows);
ASSERT_EQ(in_csr_matrix.num_rows, coo.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 1);
in_csr_matrix = g->GetCSCMatrix(0);
ASSERT_EQ(in_csr_matrix.num_cols, coo.num_rows);
ASSERT_EQ(in_csr_matrix.num_rows, coo.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 5);
}
template <typename IdType>
void _TestUnitGraph_GetOutCSR(DGLContext ctx) {
const aten::CSRMatrix &csr = CSR1<IdType>(ctx);
const aten::COOMatrix &coo = COO1<IdType>(ctx);
auto g = CreateFromCSC(2, csr);
auto g_ptr = g->GetGraphInFormat(CSR_CODE);
auto out_csr_matrix = g_ptr->GetCSRMatrix(0);
ASSERT_EQ(out_csr_matrix.num_cols, csr.num_rows);
ASSERT_EQ(out_csr_matrix.num_rows, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 4);
out_csr_matrix = g->GetCSRMatrix(0);
ASSERT_EQ(out_csr_matrix.num_cols, csr.num_rows);
ASSERT_EQ(out_csr_matrix.num_rows, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 6);
// test out csr
g = CreateFromCSR(2, csr);
out_csr_matrix = g->GetCSRMatrix(0);
ASSERT_EQ(out_csr_matrix.num_rows, csr.num_rows);
ASSERT_EQ(out_csr_matrix.num_cols, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 2);
// test out coo
g = CreateFromCOO(2, coo);
g_ptr = g->GetGraphInFormat(CSR_CODE);
out_csr_matrix = g_ptr->GetCSRMatrix(0);
ASSERT_EQ(out_csr_matrix.num_rows, coo.num_rows);
ASSERT_EQ(out_csr_matrix.num_cols, coo.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 1);
out_csr_matrix = g->GetCSRMatrix(0);
ASSERT_EQ(out_csr_matrix.num_rows, coo.num_rows);
ASSERT_EQ(out_csr_matrix.num_cols, coo.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 3);
}
template <typename IdType>
void _TestUnitGraph_GetCOO(DGLContext ctx) {
const aten::CSRMatrix &csr = CSR1<IdType>(ctx);
const aten::COOMatrix &coo = COO1<IdType>(ctx);
auto g = CreateFromCSC(2, csr);
auto g_ptr = g->GetGraphInFormat(COO_CODE);
auto out_coo_matrix = g_ptr->GetCOOMatrix(0);
ASSERT_EQ(out_coo_matrix.num_cols, csr.num_rows);
ASSERT_EQ(out_coo_matrix.num_rows, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 4);
out_coo_matrix = g->GetCOOMatrix(0);
ASSERT_EQ(out_coo_matrix.num_cols, csr.num_rows);
ASSERT_EQ(out_coo_matrix.num_rows, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 5);
// test out csr
g = CreateFromCSR(2, csr);
g_ptr = g->GetGraphInFormat(COO_CODE);
out_coo_matrix = g_ptr->GetCOOMatrix(0);
ASSERT_EQ(out_coo_matrix.num_rows, csr.num_rows);
ASSERT_EQ(out_coo_matrix.num_cols, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 2);
out_coo_matrix = g->GetCOOMatrix(0);
ASSERT_EQ(out_coo_matrix.num_rows, csr.num_rows);
ASSERT_EQ(out_coo_matrix.num_cols, csr.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 3);
// test out coo
g = CreateFromCOO(2, coo);
out_coo_matrix = g->GetCOOMatrix(0);
ASSERT_EQ(out_coo_matrix.num_rows, coo.num_rows);
ASSERT_EQ(out_coo_matrix.num_cols, coo.num_cols);
ASSERT_EQ(g->GetCreatedFormats(), 1);
}
template <typename IdType>
void _TestUnitGraph_Reserve(DGLContext ctx) {
const aten::CSRMatrix &csr = CSR1<IdType>(ctx);
const aten::COOMatrix &coo = COO1<IdType>(ctx);
auto g = CreateFromCSC(2, csr);
ASSERT_EQ(g->GetCreatedFormats(), 4);
auto r_g =
std::dynamic_pointer_cast<UnitGraph>(g->GetRelationGraph(0))->Reverse();
ASSERT_EQ(r_g->GetCreatedFormats(), 2);
aten::CSRMatrix g_in_csr = g->GetCSCMatrix(0);
aten::CSRMatrix r_g_out_csr = r_g->GetCSRMatrix(0);
ASSERT_TRUE(g_in_csr.indptr->data == r_g_out_csr.indptr->data);
ASSERT_TRUE(g_in_csr.indices->data == r_g_out_csr.indices->data);
aten::CSRMatrix g_out_csr = g->GetCSRMatrix(0);
ASSERT_EQ(g->GetCreatedFormats(), 6);
ASSERT_EQ(r_g->GetCreatedFormats(), 6);
aten::CSRMatrix r_g_in_csr = r_g->GetCSCMatrix(0);
ASSERT_TRUE(g_out_csr.indptr->data == r_g_in_csr.indptr->data);
ASSERT_TRUE(g_out_csr.indices->data == r_g_in_csr.indices->data);
aten::COOMatrix g_coo = g->GetCOOMatrix(0);
ASSERT_EQ(g->GetCreatedFormats(), 7);
ASSERT_EQ(r_g->GetCreatedFormats(), 6);
aten::COOMatrix r_g_coo = r_g->GetCOOMatrix(0);
ASSERT_EQ(r_g->GetCreatedFormats(), 7);
ASSERT_EQ(g_coo.num_rows, r_g_coo.num_cols);
ASSERT_EQ(g_coo.num_cols, r_g_coo.num_rows);
ASSERT_TRUE(ArrayEQ<IdType>(g_coo.row, r_g_coo.col));
ASSERT_TRUE(ArrayEQ<IdType>(g_coo.col, r_g_coo.row));
// test out csr
g = CreateFromCSR(2, csr);
ASSERT_EQ(g->GetCreatedFormats(), 2);
r_g = std::dynamic_pointer_cast<UnitGraph>(g->GetRelationGraph(0))->Reverse();
ASSERT_EQ(r_g->GetCreatedFormats(), 4);
g_out_csr = g->GetCSRMatrix(0);
r_g_in_csr = r_g->GetCSCMatrix(0);
ASSERT_TRUE(g_out_csr.indptr->data == r_g_in_csr.indptr->data);
ASSERT_TRUE(g_out_csr.indices->data == r_g_in_csr.indices->data);
g_in_csr = g->GetCSCMatrix(0);
ASSERT_EQ(g->GetCreatedFormats(), 6);
ASSERT_EQ(r_g->GetCreatedFormats(), 6);
r_g_out_csr = r_g->GetCSRMatrix(0);
ASSERT_TRUE(g_in_csr.indptr->data == r_g_out_csr.indptr->data);
ASSERT_TRUE(g_in_csr.indices->data == r_g_out_csr.indices->data);
g_coo = g->GetCOOMatrix(0);
ASSERT_EQ(g->GetCreatedFormats(), 7);
ASSERT_EQ(r_g->GetCreatedFormats(), 6);
r_g_coo = r_g->GetCOOMatrix(0);
ASSERT_EQ(r_g->GetCreatedFormats(), 7);
ASSERT_EQ(g_coo.num_rows, r_g_coo.num_cols);
ASSERT_EQ(g_coo.num_cols, r_g_coo.num_rows);
ASSERT_TRUE(ArrayEQ<IdType>(g_coo.row, r_g_coo.col));
ASSERT_TRUE(ArrayEQ<IdType>(g_coo.col, r_g_coo.row));
// test out coo
g = CreateFromCOO(2, coo);
ASSERT_EQ(g->GetCreatedFormats(), 1);
r_g = std::dynamic_pointer_cast<UnitGraph>(g->GetRelationGraph(0))->Reverse();
ASSERT_EQ(r_g->GetCreatedFormats(), 1);
g_coo = g->GetCOOMatrix(0);
r_g_coo = r_g->GetCOOMatrix(0);
ASSERT_EQ(g_coo.num_rows, r_g_coo.num_cols);
ASSERT_EQ(g_coo.num_cols, r_g_coo.num_rows);
ASSERT_TRUE(g_coo.row->data == r_g_coo.col->data);
ASSERT_TRUE(g_coo.col->data == r_g_coo.row->data);
g_in_csr = g->GetCSCMatrix(0);
ASSERT_EQ(g->GetCreatedFormats(), 5);
ASSERT_EQ(r_g->GetCreatedFormats(), 3);
r_g_out_csr = r_g->GetCSRMatrix(0);
ASSERT_TRUE(g_in_csr.indptr->data == r_g_out_csr.indptr->data);
ASSERT_TRUE(g_in_csr.indices->data == r_g_out_csr.indices->data);
g_out_csr = g->GetCSRMatrix(0);
ASSERT_EQ(g->GetCreatedFormats(), 7);
ASSERT_EQ(r_g->GetCreatedFormats(), 7);
r_g_in_csr = r_g->GetCSCMatrix(0);
ASSERT_TRUE(g_out_csr.indptr->data == r_g_in_csr.indptr->data);
ASSERT_TRUE(g_out_csr.indices->data == r_g_in_csr.indices->data);
}
template <typename IdType>
void _TestUnitGraph_CopyTo(
const DGLContext &src_ctx, const DGLContext &dst_ctx) {
const aten::CSRMatrix &csr = CSR1<IdType>(src_ctx);
const aten::COOMatrix &coo = COO1<IdType>(src_ctx);
auto device = dgl::runtime::DeviceAPI::Get(dst_ctx);
// We don't allow SetStream in DGL for now.
auto stream = nullptr;
auto g = dgl::UnitGraph::CreateFromCSC(2, csr);
ASSERT_EQ(g->GetCreatedFormats(), 4);
auto cg = dgl::UnitGraph::CopyTo(g, dst_ctx);
device->StreamSync(dst_ctx, stream);
ASSERT_EQ(cg->GetCreatedFormats(), 4);
g = dgl::UnitGraph::CreateFromCSR(2, csr);
ASSERT_EQ(g->GetCreatedFormats(), 2);
cg = dgl::UnitGraph::CopyTo(g, dst_ctx);
device->StreamSync(dst_ctx, stream);
ASSERT_EQ(cg->GetCreatedFormats(), 2);
g = dgl::UnitGraph::CreateFromCOO(2, coo);
ASSERT_EQ(g->GetCreatedFormats(), 1);
cg = dgl::UnitGraph::CopyTo(g, dst_ctx);
device->StreamSync(dst_ctx, stream);
ASSERT_EQ(cg->GetCreatedFormats(), 1);
}
TEST(UniGraphTest, TestUnitGraph_CopyTo) {
_TestUnitGraph_CopyTo<int32_t>(CPU, CPU);
_TestUnitGraph_CopyTo<int64_t>(CPU, CPU);
#ifdef DGL_USE_CUDA
_TestUnitGraph_CopyTo<int32_t>(CPU, GPU);
_TestUnitGraph_CopyTo<int32_t>(GPU, GPU);
_TestUnitGraph_CopyTo<int32_t>(GPU, CPU);
_TestUnitGraph_CopyTo<int64_t>(CPU, GPU);
_TestUnitGraph_CopyTo<int64_t>(GPU, GPU);
_TestUnitGraph_CopyTo<int64_t>(GPU, CPU);
#endif
}
TEST(UniGraphTest, TestUnitGraph_InOutDegrees) {
_TestUnitGraph_InOutDegrees<int32_t>(CPU);
_TestUnitGraph_InOutDegrees<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestUnitGraph_InOutDegrees<int32_t>(GPU);
_TestUnitGraph_InOutDegrees<int64_t>(GPU);
#endif
}
TEST(UniGraphTest, TestUnitGraph_Create) {
_TestUnitGraph<int32_t>(CPU);
_TestUnitGraph<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestUnitGraph<int32_t>(GPU);
_TestUnitGraph<int64_t>(GPU);
#endif
}
TEST(UniGraphTest, TestUnitGraph_GetInCSR) {
_TestUnitGraph_GetInCSR<int32_t>(CPU);
_TestUnitGraph_GetInCSR<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestUnitGraph_GetInCSR<int32_t>(GPU);
_TestUnitGraph_GetInCSR<int64_t>(GPU);
#endif
}
TEST(UniGraphTest, TestUnitGraph_GetOutCSR) {
_TestUnitGraph_GetOutCSR<int32_t>(CPU);
_TestUnitGraph_GetOutCSR<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestUnitGraph_GetOutCSR<int32_t>(GPU);
_TestUnitGraph_GetOutCSR<int64_t>(GPU);
#endif
}
TEST(UniGraphTest, TestUnitGraph_GetCOO) {
_TestUnitGraph_GetCOO<int32_t>(CPU);
_TestUnitGraph_GetCOO<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestUnitGraph_GetCOO<int32_t>(GPU);
_TestUnitGraph_GetCOO<int64_t>(GPU);
#endif
}
TEST(UniGraphTest, TestUnitGraph_Reserve) {
_TestUnitGraph_Reserve<int32_t>(CPU);
_TestUnitGraph_Reserve<int64_t>(CPU);
#ifdef DGL_USE_CUDA
_TestUnitGraph_Reserve<int32_t>(GPU);
_TestUnitGraph_Reserve<int64_t>(GPU);
#endif
}
+167
View File
@@ -0,0 +1,167 @@
#include <dgl/array.h>
#include <dgl/immutable_graph.h>
#include <dgl/zerocopy_serializer.h>
#include <dmlc/memory_io.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include "../../src/graph/heterograph.h"
#include "../../src/graph/unit_graph.h"
#include "./common.h"
#ifndef _WIN32
using namespace dgl;
using namespace dgl::aten;
using namespace dmlc;
// Function to convert an idarray to string
std::string IdArrayToStr(IdArray arr) {
arr = arr.CopyTo(DGLContext{kDGLCPU, 0});
int64_t len = arr->shape[0];
std::ostringstream oss;
oss << "(" << len << ")[";
if (arr->dtype.bits == 32) {
int32_t *data = static_cast<int32_t *>(arr->data);
for (int64_t i = 0; i < len; ++i) {
oss << data[i] << " ";
}
} else {
int64_t *data = static_cast<int64_t *>(arr->data);
for (int64_t i = 0; i < len; ++i) {
oss << data[i] << " ";
}
}
oss << "]";
return oss.str();
}
TEST(ZeroCopySerialize, NDArray) {
auto tensor1 = VecToIdArray<int64_t>({1, 2, 5, 3});
auto tensor2 = VecToIdArray<int64_t>({6, 6, 5, 7});
std::string nonzerocopy_blob;
dmlc::MemoryStringStream ifs(&nonzerocopy_blob);
static_cast<dmlc::Stream *>(&ifs)->Write(tensor1);
static_cast<dmlc::Stream *>(&ifs)->Write(tensor2);
std::string zerocopy_blob;
StreamWithBuffer zc_write_strm(&zerocopy_blob, true);
zc_write_strm.Write(tensor1);
zc_write_strm.Write(tensor2);
EXPECT_EQ(nonzerocopy_blob.size() - zerocopy_blob.size(), 126)
<< "Invalid save";
std::vector<void *> new_ptr_list;
// Use memcpy to mimic remote machine reconstruction
for (auto ptr : zc_write_strm.buffer_list()) {
auto new_ptr = malloc(ptr.size);
memcpy(new_ptr, ptr.data, ptr.size);
new_ptr_list.emplace_back(new_ptr);
}
NDArray loadtensor1, loadtensor2;
StreamWithBuffer zc_read_strm(&zerocopy_blob, new_ptr_list);
zc_read_strm.Read(&loadtensor1);
zc_read_strm.Read(&loadtensor2);
}
TEST(ZeroCopySerialize, ZeroShapeNDArray) {
auto tensor1 = VecToIdArray<int64_t>({6, 6, 5, 7});
auto tensor2 = VecToIdArray<int64_t>({});
auto tensor3 = VecToIdArray<int64_t>({6, 6, 2, 7});
std::vector<NDArray> ndvec;
ndvec.push_back(tensor1);
ndvec.push_back(tensor2);
ndvec.push_back(tensor3);
std::string zerocopy_blob;
StreamWithBuffer zc_write_strm(&zerocopy_blob, true);
zc_write_strm.Write(ndvec);
std::vector<void *> new_ptr_list;
// Use memcpy to mimic remote machine reconstruction
for (auto ptr : zc_write_strm.buffer_list()) {
auto new_ptr = malloc(ptr.size);
memcpy(new_ptr, ptr.data, ptr.size);
new_ptr_list.emplace_back(new_ptr);
}
std::vector<NDArray> ndvec_read;
StreamWithBuffer zc_read_strm(&zerocopy_blob, new_ptr_list);
zc_read_strm.Read(&ndvec_read);
EXPECT_EQ(ndvec_read[1]->ndim, 1);
EXPECT_EQ(ndvec_read[1]->shape[0], 0);
}
TEST(ZeroCopySerialize, SharedMem) {
auto tensor1 = VecToIdArray<int64_t>({1, 2, 5, 3});
DGLDataType dtype = {kDGLInt, 64, 1};
std::vector<int64_t> shape{4};
DGLContext cpu_ctx = {kDGLCPU, 0};
auto shared_tensor =
NDArray::EmptyShared("test", shape, dtype, cpu_ctx, true);
shared_tensor.CopyFrom(tensor1);
std::string nonzerocopy_blob;
dmlc::MemoryStringStream ifs(&nonzerocopy_blob);
static_cast<dmlc::Stream *>(&ifs)->Write(shared_tensor);
std::string zerocopy_blob;
StreamWithBuffer zc_write_strm(&zerocopy_blob, false);
zc_write_strm.Write(shared_tensor);
EXPECT_EQ(nonzerocopy_blob.size() - zerocopy_blob.size(), 51)
<< "Invalid save";
NDArray loadtensor1;
StreamWithBuffer zc_read_strm = StreamWithBuffer(&zerocopy_blob, false);
zc_read_strm.Read(&loadtensor1);
}
TEST(ZeroCopySerialize, HeteroGraph) {
auto src = VecToIdArray<int64_t>({1, 2, 5, 3});
auto dst = VecToIdArray<int64_t>({1, 6, 2, 6});
auto mg1 = dgl::UnitGraph::CreateFromCOO(2, 9, 8, src, dst);
src = VecToIdArray<int64_t>({6, 2, 5, 1, 8});
dst = VecToIdArray<int64_t>({5, 2, 4, 8, 0});
auto mg2 = dgl::UnitGraph::CreateFromCOO(1, 9, 9, src, dst);
std::vector<HeteroGraphPtr> relgraphs;
relgraphs.push_back(mg1);
relgraphs.push_back(mg2);
src = VecToIdArray<int64_t>({0, 0});
dst = VecToIdArray<int64_t>({1, 0});
auto meta_gptr = ImmutableGraph::CreateFromCOO(3, src, dst);
auto hrptr = std::make_shared<HeteroGraph>(meta_gptr, relgraphs);
std::string nonzerocopy_blob;
dmlc::MemoryStringStream ifs(&nonzerocopy_blob);
static_cast<dmlc::Stream *>(&ifs)->Write(hrptr);
std::string zerocopy_blob;
StreamWithBuffer zc_write_strm(&zerocopy_blob, true);
zc_write_strm.Write(hrptr);
EXPECT_EQ(nonzerocopy_blob.size() - zerocopy_blob.size(), 745)
<< "Invalid save";
std::vector<void *> new_ptr_list;
// Use memcpy to mimic remote machine reconstruction
for (auto ptr : zc_write_strm.buffer_list()) {
auto new_ptr = malloc(ptr.size);
memcpy(new_ptr, ptr.data, ptr.size);
new_ptr_list.emplace_back(new_ptr);
}
auto gptr = dgl::Serializer::make_shared<HeteroGraph>();
StreamWithBuffer zc_read_strm(&zerocopy_blob, new_ptr_list);
zc_read_strm.Read(&gptr);
EXPECT_EQ(gptr->NumVertices(0), 9);
EXPECT_EQ(gptr->NumVertices(1), 8);
}
#endif // _WIN32