Add unit test for graph connectivity with data from the ECL_CC paper

This commit is contained in:
Li-Ta Lo 2020-08-27 17:37:08 -06:00
parent 17047f456d
commit af53fb736f
6 changed files with 123 additions and 7 deletions

3
data/data/third_party/ecl_cc/README vendored Normal file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6f5e6e3dc559fefc7990daaec071fcd620f620e5ab8652dddaa6b43ca4ba08e7
size 222

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:892470e152ccd46ddcca70e26bcd88816c247f08c496319cea80864b6b94ce46
size 3596536

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1058d22553440f11d3077e70ac8d3a06c6f59107f2dee3fa889fb5933a2a2483
size 1465043532

@ -81,7 +81,7 @@ public:
auto root_u = findRoot(parents, u);
auto root_v = findRoot(parents, v);
// There is a potential concurrent write data race as it is possible for
// Case 3. There is a potential concurrent write data race as it is possible for
// two threads to try to change the same old root to different new roots,
// e.g. threadA calls parents.Set(root, rootB) while threadB calls
// parents(root, rootB) where rootB < root and rootC < root (but the order

@ -63,11 +63,14 @@ public:
// Case 1, Two threads calling Unite(u, v) concurrently.
// Problem: One thread might attach u to v while the other thread attach
// v to u, causing a cycle in the Union-Find data structure.
// Resolution: This is not necessary a data race issue. This is resolved by
// "linking by index" as in SV Jayanti et.al. with less than as the total order.
// Resolution: This is not so much a race condition as a problem with the
// consistency of the algorithm that can also happen in serial. This is
// resolved by "linking by index" as in SV Jayanti et.al. with less than
// as the total order.
// The two threads will make the same decision on how to Unite the two tree
// (e.g. from root with larger id to root with smaller id.) This avoids cycles in
// the resulting graph and maintains the rooted forest structure of Union-Find.
// (e.g. from root with larger id to root with smaller id.) This avoids
// cycles in the resulting graph and maintains the rooted forest structure
// of Union-Find.
// Case 2, T0 calling Unite(u, v), T1 calling Unite(u, w) and T2 calling
// Unite(v, s) concurrently.
@ -87,7 +90,7 @@ public:
auto root_u = findRoot(parents, u);
auto root_v = findRoot(parents, v);
// There is a potential concurrent write data race as it is possible for
// Case 3. There is a potential concurrent write data race as it is possible for
// two threads to try to change the same old root to different new roots,
// e.g. threadA calls parents.Set(root, rootB) while threadB calls
// parents(root, rootB) where rootB < root and rootC < root (but the order

@ -12,10 +12,108 @@
#include <vtkm/worklet/connectivities/GraphConnectivity.h>
class AdjacentDifference : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn index, WholeArrayIn counts, FieldOut outputCount);
using ExecutionSignature = void(_1, _2, _3);
using InputDomain = _1;
template <typename WholeArrayType>
VTKM_EXEC void operator()(const vtkm::Id& index,
const WholeArrayType& counts,
int& difference) const
{
difference = counts.Get(index + 1) - counts.Get(index);
}
};
class SameComponent : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn start,
FieldIn degree,
WholeArrayIn conns,
WholeArrayIn comps,
AtomicArrayInOut same);
using ExecutionSignature = void(WorkIndex, _1, _2, _3, _4, _5);
template <typename Conn, typename Comp, typename AtomicSame>
VTKM_EXEC void operator()(vtkm::Id index,
int start,
int degree,
const Conn& conns,
const Comp& comps,
AtomicSame& same) const
{
for (vtkm::Id offset = start; offset < start + degree; ++offset)
{
vtkm::Id neighbor = conns.Get(offset);
if (comps.Get(index) != comps.Get(neighbor))
{
same.Set(0, 0);
}
}
}
};
class TestGraphConnectivity
{
public:
void operator()() const
void TestECL_CC(const std::string& filename, int ncomps) const
{
auto pathname =
vtkm::cont::testing::Testing::GetTestDataBasePath() + "/third_party/ecl_cc/" + filename;
std::ifstream stream(pathname, std::ios_base::in | std::ios_base::binary);
int nnodes;
stream.read(reinterpret_cast<char*>(&nnodes), sizeof(nnodes));
int nedges;
stream.read(reinterpret_cast<char*>(&nedges), sizeof(nedges));
// CSR, there is one more element in offsets thant the actual number of nodes.
std::vector<int> offsets(nnodes + 1);
std::vector<int> conns(nedges);
stream.read(reinterpret_cast<char*>(offsets.data()), (nnodes + 1) * sizeof(int));
stream.read(reinterpret_cast<char*>(conns.data()), nedges * sizeof(int));
vtkm::cont::ArrayHandle<int> counts_h;
vtkm::cont::Invoker invoke;
invoke(AdjacentDifference{},
vtkm::cont::make_ArrayHandleCounting(0, 1, nnodes),
vtkm::cont::make_ArrayHandle<int>(offsets, vtkm::CopyFlag::On),
counts_h);
offsets.pop_back();
vtkm::cont::ArrayHandle<int> offsets_h =
vtkm::cont::make_ArrayHandle(offsets, vtkm::CopyFlag::On);
vtkm::cont::ArrayHandle<int> conns_h = vtkm::cont::make_ArrayHandle(conns, vtkm::CopyFlag::Off);
vtkm::cont::ArrayHandle<vtkm::Id> comps_h;
vtkm::worklet::connectivity::GraphConnectivity().Run(counts_h, offsets_h, conns_h, comps_h);
VTKM_TEST_ASSERT(vtkm::cont::Algorithm::Reduce(comps_h, 0, vtkm::Maximum{}) == ncomps - 1,
"number of components mismatch");
vtkm::cont::ArrayHandle<vtkm::UInt32> atomicSame;
atomicSame.Allocate(1);
atomicSame.WritePortal().Set(0, 1);
invoke(SameComponent{}, offsets_h, counts_h, conns_h, comps_h, atomicSame);
VTKM_TEST_ASSERT(atomicSame.ReadPortal().Get(0) == 1,
"Neighboring nodes don't have the same component id");
}
void TestECL_CC_DataSets() const
{
TestECL_CC("internet.egr", 1);
TestECL_CC("kron_g500-logn21.egr", 553159);
}
void TestSimpleGraph() const
{
vtkm::cont::ArrayHandle<vtkm::Id> counts_h =
vtkm::cont::make_ArrayHandle<vtkm::Id>({ 1, 1, 2, 2, 2 });
@ -32,6 +130,12 @@ public:
VTKM_TEST_ASSERT(comps.ReadPortal().Get(i) == 0, "Components has unexpected value.");
}
}
void operator()() const
{
TestSimpleGraph();
TestECL_CC_DataSets();
}
};
int UnitTestGraphConnectivity(int argc, char* argv[])