vtk-m2/vtkm/cont/testing/TestingDeviceAdapter.h
Robert Maynard ee4e490f1d Moved SMP atomic operations from TBB to General.
Even when running with the serial backend, the compiler might enable SIMD
vectorization when optimizations are turned on. When this occurs, we need
to use properly atomic Add's and CAE's.
2016-03-14 10:40:42 -04:00

1749 lines
62 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//
// Copyright 2014 Sandia Corporation.
// Copyright 2014 UT-Battelle, LLC.
// Copyright 2014 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_cont_testing_TestingDeviceAdapter_h
#define vtk_m_cont_testing_TestingDeviceAdapter_h
#include <vtkm/TypeTraits.h>
#include <vtkm/BinaryPredicates.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/ArrayHandlePermutation.h>
#include <vtkm/cont/ArrayHandleZip.h>
#include <vtkm/cont/ArrayPortalToIterators.h>
#include <vtkm/cont/ErrorControlBadAllocation.h>
#include <vtkm/cont/ErrorExecution.h>
#include <vtkm/cont/RuntimeDeviceInformation.h>
#include <vtkm/cont/StorageBasic.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/internal/DeviceAdapterError.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/exec/AtomicArray.h>
#include <algorithm>
#include <cmath>
#include <utility>
#include <vector>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#undef WIN32_LEAN_AND_MEAN
#endif
namespace vtkm {
namespace cont {
namespace testing {
namespace comparison {
struct MaxValue
{
template<typename T>
VTKM_EXEC_CONT_EXPORT T operator()(const T& a,const T& b) const
{
return (a > b) ? a : b;
}
};
}
#define ERROR_MESSAGE "Got an error."
#define ARRAY_SIZE 1000
#define OFFSET 1000
#define DIM_SIZE 128
/// This class has a single static member, Run, that tests the templated
/// DeviceAdapter for conformance.
///
template<class DeviceAdapterTag>
struct TestingDeviceAdapter
{
private:
typedef vtkm::cont::StorageTagBasic StorageTag;
typedef vtkm::cont::ArrayHandle<vtkm::Id, StorageTag>
IdArrayHandle;
typedef vtkm::cont::ArrayHandle<vtkm::FloatDefault,StorageTag>
ScalarArrayHandle;
typedef vtkm::cont::internal::ArrayManagerExecution<
vtkm::Id, StorageTag, DeviceAdapterTag>
IdArrayManagerExecution;
typedef vtkm::cont::internal::Storage<vtkm::Id, StorageTag> IdStorage;
typedef typename IdArrayHandle::template ExecutionTypes<DeviceAdapterTag>
::Portal IdPortalType;
typedef typename IdArrayHandle::template ExecutionTypes<DeviceAdapterTag>
::PortalConst IdPortalConstType;
typedef vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapterTag>
Algorithm;
public:
// Cuda kernels have to be public (in Cuda 4.0).
struct CopyArrayKernel
{
VTKM_CONT_EXPORT
CopyArrayKernel(const IdPortalConstType &input,
const IdPortalType &output)
: InputArray(input), OutputArray(output) { }
VTKM_EXEC_EXPORT void operator()(
vtkm::Id index,
const vtkm::exec::internal::ErrorMessageBuffer &) const
{
this->OutputArray.Set(index, this->InputArray.Get(index));
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &) { }
IdPortalConstType InputArray;
IdPortalType OutputArray;
};
struct ClearArrayKernel
{
VTKM_CONT_EXPORT
ClearArrayKernel(const IdPortalType &array) : Array(array), Dims() { }
VTKM_CONT_EXPORT
ClearArrayKernel(const IdPortalType &array,
const vtkm::Id3& dims) : Array(array), Dims(dims) { }
VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const
{
this->Array.Set(index, OFFSET);
}
VTKM_EXEC_EXPORT void operator()(vtkm::Id3 index) const
{
//convert from id3 to id
vtkm::Id flatIndex =
index[0]+ this->Dims[0]*(index[1]+ this->Dims[1]*index[2]);
this->operator()(flatIndex);
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &) { }
IdPortalType Array;
vtkm::Id3 Dims;
};
struct ClearArrayMapKernel //: public vtkm::exec::WorkletMapField
{
// typedef void ControlSignature(Field(Out));
// typedef void ExecutionSignature(_1);
template<typename T>
VTKM_EXEC_EXPORT void operator()(T& value) const
{
value = OFFSET;
}
};
struct AddArrayKernel
{
VTKM_CONT_EXPORT
AddArrayKernel(const IdPortalType &array) : Array(array), Dims() { }
VTKM_CONT_EXPORT
AddArrayKernel(const IdPortalType &array,
const vtkm::Id3& dims) : Array(array), Dims(dims) { }
VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const
{
this->Array.Set(index, this->Array.Get(index) + index);
}
VTKM_EXEC_EXPORT void operator()(vtkm::Id3 index) const
{
//convert from id3 to id
vtkm::Id flatIndex =
index[0]+ this->Dims[0]*(index[1]+ this->Dims[1]*index[2]);
this->operator()(flatIndex);
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &) { }
IdPortalType Array;
vtkm::Id3 Dims;
};
struct OneErrorKernel
{
VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const
{
if (index == ARRAY_SIZE/2)
{
this->ErrorMessage.RaiseError(ERROR_MESSAGE);
}
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &errorMessage)
{
this->ErrorMessage = errorMessage;
}
vtkm::exec::internal::ErrorMessageBuffer ErrorMessage;
};
struct AllErrorKernel
{
VTKM_EXEC_EXPORT void operator()(vtkm::Id vtkmNotUsed(index)) const
{
this->ErrorMessage.RaiseError(ERROR_MESSAGE);
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &errorMessage)
{
this->ErrorMessage = errorMessage;
}
vtkm::exec::internal::ErrorMessageBuffer ErrorMessage;
};
struct OffsetPlusIndexKernel
{
VTKM_CONT_EXPORT
OffsetPlusIndexKernel(const IdPortalType &array) : Array(array) { }
VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const
{
this->Array.Set(index, OFFSET + index);
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &) { }
IdPortalType Array;
};
struct MarkOddNumbersKernel
{
VTKM_CONT_EXPORT
MarkOddNumbersKernel(const IdPortalType &array) : Array(array) { }
VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const
{
this->Array.Set(index, index%2);
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &) { }
IdPortalType Array;
};
struct FuseAll
{
template<typename T>
VTKM_EXEC_EXPORT bool operator()(const T&, const T&) const
{
//binary predicates for unique return true if they are the same
return true;
}
};
template<typename T>
struct AtomicKernel
{
VTKM_CONT_EXPORT
AtomicKernel(const vtkm::exec::AtomicArray<T,DeviceAdapterTag> &array)
: AArray(array)
{ }
VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const
{
T value = (T) index;
this->AArray.Add(0, value);
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &) { }
vtkm::exec::AtomicArray<T,DeviceAdapterTag> AArray;
};
template<typename T>
struct AtomicCASKernel
{
VTKM_CONT_EXPORT
AtomicCASKernel(const vtkm::exec::AtomicArray<T,DeviceAdapterTag> &array)
: AArray(array)
{ }
VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const
{
T value = (T) index;
//Get the old value from the array with a no-op
T oldValue = this->AArray.Add(0,T(0));
//This creates an atomic add using the CAS operatoin
T assumed = T(0);
do
{
assumed = oldValue;
oldValue = this->AArray.CompareAndSwap(0, (assumed + value) , assumed);
} while (assumed != oldValue);
}
VTKM_CONT_EXPORT void SetErrorMessageBuffer(
const vtkm::exec::internal::ErrorMessageBuffer &) { }
vtkm::exec::AtomicArray<T,DeviceAdapterTag> AArray;
};
private:
static VTKM_CONT_EXPORT void TestDeviceAdapterTag()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing device adapter tag" << std::endl;
typedef vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag> Traits;
typedef vtkm::cont::DeviceAdapterTraits<
vtkm::cont::DeviceAdapterTagError> ErrorTraits;
VTKM_TEST_ASSERT(Traits::GetId() == Traits::GetId(),
"Device adapter Id does not equal itself.");
VTKM_TEST_ASSERT(Traits::GetId() != ErrorTraits::GetId(),
"Device adapter Id not distinguishable from others.");
VTKM_TEST_ASSERT(Traits::GetName() == Traits::GetName(),
"Device adapter Name does not equal itself.");
VTKM_TEST_ASSERT(Traits::GetName() != ErrorTraits::GetName(),
"Device adapter Name not distinguishable from others.");
}
// Note: this test does not actually test to make sure the data is available
// in the execution environment. It tests to make sure data gets to the array
// and back, but it is possible that the data is not available in the
// execution environment.
static VTKM_CONT_EXPORT void TestArrayManagerExecution()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayManagerExecution" << std::endl;
typedef vtkm::cont::internal::ArrayManagerExecution<
vtkm::Id,StorageTagBasic,DeviceAdapterTag>
ArrayManagerExecution;
typedef vtkm::cont::internal::Storage<vtkm::Id,StorageTagBasic> StorageType;
// Create original input array.
StorageType storage;
storage.Allocate(ARRAY_SIZE*2);
StorageType::PortalType portal = storage.GetPortal();
VTKM_TEST_ASSERT(portal.GetNumberOfValues() == ARRAY_SIZE*2,
"Storage portal has unexpected size.");
for (vtkm::Id index = 0; index < ARRAY_SIZE; index++)
{
portal.Set(index, TestValue(index, vtkm::Id()));
}
ArrayManagerExecution manager(&storage);
// Do an operation just so we know the values are placed in the execution
// environment and they change. We are only calling on half the array
// because we are about to shrink.
Algorithm::Schedule(AddArrayKernel(manager.PrepareForInPlace(true)),
ARRAY_SIZE);
// Change size.
manager.Shrink(ARRAY_SIZE);
VTKM_TEST_ASSERT(manager.GetNumberOfValues() == ARRAY_SIZE,
"Shrink did not set size of array manager correctly.");
// Get the array back and check its values. We have to get it back into
// the same storage since some ArrayManagerExecution classes will expect
// that.
manager.RetrieveOutputData(&storage);
VTKM_TEST_ASSERT(storage.GetNumberOfValues() == ARRAY_SIZE,
"Storage has wrong number of values after execution "
"array shrink.");
// Check array.
StorageType::PortalConstType checkPortal = storage.GetPortalConst();
VTKM_TEST_ASSERT(checkPortal.GetNumberOfValues() == ARRAY_SIZE,
"Storage portal wrong size.");
for (vtkm::Id index = 0; index < ARRAY_SIZE; index++)
{
VTKM_TEST_ASSERT(
checkPortal.Get(index) == TestValue(index, vtkm::Id()) + index,
"Did not get correct values from array.");
}
}
static VTKM_CONT_EXPORT void TestOutOfMemory()
{
// Only test out of memory with 64 bit ids. If there are 32 bit ids on
// a 64 bit OS (common), it is simply too hard to get a reliable allocation
// that is too much memory.
#ifdef VTKM_USE_64BIT_IDS
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Out of Memory" << std::endl;
try
{
std::cout << "Do array allocation that should fail." << std::endl;
vtkm::cont::internal::Storage<
vtkm::Vec<vtkm::Float32, 4>, StorageTagBasic> supportArray;
vtkm::cont::internal::ArrayManagerExecution<
vtkm::Vec<vtkm::Float32, 4>, StorageTagBasic, DeviceAdapterTag>
bigManager(&supportArray);
const vtkm::Id bigSize = 0x7FFFFFFFFFFFFFFFLL;
bigManager.PrepareForOutput(bigSize);
// It does not seem reasonable to get here. The previous call should fail.
VTKM_TEST_FAIL("A ridiculously sized allocation succeeded. Either there "
"was a failure that was not reported but should have been "
"or the width of vtkm::Id is not large enough to express all "
"array sizes.");
}
catch (vtkm::cont::ErrorControlBadAllocation error)
{
std::cout << "Got the expected error: " << error.GetMessage() << std::endl;
}
#else
std::cout << "--------- Skipping out of memory test" << std::endl;
#endif
}
VTKM_CONT_EXPORT
static void TestTimer()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Timer" << std::endl;
vtkm::cont::Timer<DeviceAdapterTag> timer;
std::cout << "Timer started. Sleeping..." << std::endl;
#ifndef _WIN32
sleep(1);
#else
Sleep(1000);
#endif
std::cout << "Woke up. Check time." << std::endl;
vtkm::Float64 elapsedTime = timer.GetElapsedTime();
std::cout << "Elapsed time: " << elapsedTime << std::endl;
VTKM_TEST_ASSERT(elapsedTime > 0.999,
"Timer did not capture full second wait.");
VTKM_TEST_ASSERT(elapsedTime < 2.0,
"Timer counted too far or system really busy.");
}
VTKM_CONT_EXPORT
static void TestRuntime()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing RuntimeDeviceInformation" << std::endl;
vtkm::cont::RuntimeDeviceInformation<DeviceAdapterTag> runtime;
const bool valid_runtime = runtime.Exists();
VTKM_TEST_ASSERT(valid_runtime, "runtime detection failed for device");
}
static VTKM_CONT_EXPORT void TestAlgorithmSchedule()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing single value Scheduling with vtkm::Id" << std::endl;
{
std::cout << "Allocating execution array" << std::endl;
IdStorage storage;
IdArrayManagerExecution manager(&storage);
std::cout << "Running clear." << std::endl;
Algorithm::Schedule(ClearArrayKernel(manager.PrepareForOutput(1)), 1);
std::cout << "Running add." << std::endl;
Algorithm::Schedule(AddArrayKernel(manager.PrepareForInPlace(false)), 1);
std::cout << "Checking results." << std::endl;
manager.RetrieveOutputData(&storage);
for (vtkm::Id index = 0; index < 1; index++)
{
vtkm::Id value = storage.GetPortalConst().Get(index);
VTKM_TEST_ASSERT(value == index + OFFSET,
"Got bad value for single value scheduled kernel.");
}
} //release memory
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Schedule with vtkm::Id" << std::endl;
{
std::cout << "Allocating execution array" << std::endl;
IdStorage storage;
IdArrayManagerExecution manager(&storage);
std::cout << "Running clear." << std::endl;
Algorithm::Schedule(ClearArrayKernel(manager.PrepareForOutput(ARRAY_SIZE)),
ARRAY_SIZE);
std::cout << "Running add." << std::endl;
Algorithm::Schedule(AddArrayKernel(manager.PrepareForInPlace(false)),
ARRAY_SIZE);
std::cout << "Checking results." << std::endl;
manager.RetrieveOutputData(&storage);
for (vtkm::Id index = 0; index < ARRAY_SIZE; index++)
{
vtkm::Id value = storage.GetPortalConst().Get(index);
VTKM_TEST_ASSERT(value == index + OFFSET,
"Got bad value for scheduled kernels.");
}
} //release memory
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Schedule with a vary large Id value" << std::endl;
{
std::cout << "Allocating execution array" << std::endl;
IdStorage storage;
IdArrayManagerExecution manager(&storage);
std::cout << "Running clear." << std::endl;
//size is selected to be larger than the CUDA backend can launch in a
//single invocation when compiled for SM_2 support
const vtkm::Id size = 8400000;
Algorithm::Schedule(ClearArrayKernel(manager.PrepareForOutput(size)),
size);
std::cout << "Running add." << std::endl;
Algorithm::Schedule(AddArrayKernel(manager.PrepareForInPlace(false)),
size);
std::cout << "Checking results." << std::endl;
manager.RetrieveOutputData(&storage);
for (vtkm::Id index = 0; index < size; index++)
{
vtkm::Id value = storage.GetPortalConst().Get(index);
VTKM_TEST_ASSERT(value == index + OFFSET,
"Got bad value for scheduled kernels.");
}
} //release memory
//verify that the schedule call works with id3
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Schedule with vtkm::Id3" << std::endl;
{
std::cout << "Allocating execution array" << std::endl;
IdStorage storage;
IdArrayManagerExecution manager(&storage);
vtkm::Id3 maxRange(DIM_SIZE);
std::cout << "Running clear." << std::endl;
Algorithm::Schedule(
ClearArrayKernel(manager.PrepareForOutput(
DIM_SIZE * DIM_SIZE * DIM_SIZE), maxRange),
maxRange);
std::cout << "Running add." << std::endl;
Algorithm::Schedule(AddArrayKernel(manager.PrepareForInPlace(false), maxRange),
maxRange);
std::cout << "Checking results." << std::endl;
manager.RetrieveOutputData(&storage);
const vtkm::Id maxId = DIM_SIZE * DIM_SIZE * DIM_SIZE;
for (vtkm::Id index = 0; index < maxId; index++)
{
vtkm::Id value = storage.GetPortalConst().Get(index);
VTKM_TEST_ASSERT(value == index + OFFSET,
"Got bad value for scheduled vtkm::Id3 kernels.");
}
} //release memory
}
static VTKM_CONT_EXPORT void TestStreamCompact()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Stream Compact" << std::endl;
//test the version of compact that takes in input and uses it as a stencil
//and uses the index of each item as the value to place in the result vector
IdArrayHandle array;
IdArrayHandle result;
//construct the index array
Algorithm::Schedule(
MarkOddNumbersKernel(array.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
Algorithm::StreamCompact(array, result);
VTKM_TEST_ASSERT(result.GetNumberOfValues() == array.GetNumberOfValues()/2,
"result of compacation has an incorrect size");
for (vtkm::Id index = 0; index < result.GetNumberOfValues(); index++)
{
const vtkm::Id value = result.GetPortalConstControl().Get(index);
VTKM_TEST_ASSERT(value == (index*2)+1,
"Incorrect value in compaction results.");
}
}
static VTKM_CONT_EXPORT void TestStreamCompactWithStencil()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Stream Compact with stencil" << std::endl;
IdArrayHandle array;
IdArrayHandle stencil;
IdArrayHandle result;
//construct the index array
Algorithm::Schedule(
OffsetPlusIndexKernel(array.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
Algorithm::Schedule(
MarkOddNumbersKernel(stencil.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
Algorithm::StreamCompact(array,stencil,result);
VTKM_TEST_ASSERT(result.GetNumberOfValues() == array.GetNumberOfValues()/2,
"result of compacation has an incorrect size");
for (vtkm::Id index = 0; index < result.GetNumberOfValues(); index++)
{
const vtkm::Id value = result.GetPortalConstControl().Get(index);
VTKM_TEST_ASSERT(value == (OFFSET + (index*2)+1),
"Incorrect value in compaction result.");
}
}
static VTKM_CONT_EXPORT void TestOrderedUniqueValues()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Testing Sort, Unique, LowerBounds and UpperBounds" << std::endl;
vtkm::Id testData[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= OFFSET+(i % 50);
}
IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
//make a deep copy of input and place it into temp
IdArrayHandle temp;
Algorithm::Copy(input,temp);
Algorithm::Sort(temp);
Algorithm::Unique(temp);
IdArrayHandle handle;
IdArrayHandle handle1;
//verify lower and upper bounds work
Algorithm::LowerBounds(temp,input,handle);
Algorithm::UpperBounds(temp,input,handle1);
// Check to make sure that temp was resized correctly during Unique.
// (This was a discovered bug at one point.)
temp.GetPortalConstControl(); // Forces copy back to control.
temp.ReleaseResourcesExecution(); // Make sure not counting on execution.
VTKM_TEST_ASSERT(
temp.GetNumberOfValues() == 50,
"Unique did not resize array (or size did not copy to control).");
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
vtkm::Id value = handle.GetPortalConstControl().Get(i);
vtkm::Id value1 = handle1.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(value == i % 50, "Got bad value (LowerBounds)");
VTKM_TEST_ASSERT(value1 >= i % 50, "Got bad value (UpperBounds)");
}
std::cout << "Testing Sort, Unique, LowerBounds and UpperBounds with random values"
<< std::endl;
//now test it works when the id are not incrementing
const vtkm::Id RANDOMDATA_SIZE = 6;
vtkm::Id randomData[RANDOMDATA_SIZE];
randomData[0]=500; // 2 (lower), 3 (upper)
randomData[1]=955; // 3 (lower), 4 (upper)
randomData[2]=955; // 3 (lower), 4 (upper)
randomData[3]=120; // 0 (lower), 1 (upper)
randomData[4]=320; // 1 (lower), 2 (upper)
randomData[5]=955; // 3 (lower), 4 (upper)
//change the control structure under the handle
input = vtkm::cont::make_ArrayHandle(randomData, RANDOMDATA_SIZE);
Algorithm::Copy(input,handle);
VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE,
"Handle incorrect size after setting new control data");
Algorithm::Copy(input,handle1);
VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE,
"Handle incorrect size after setting new control data");
Algorithm::Copy(handle,temp);
VTKM_TEST_ASSERT(temp.GetNumberOfValues() == RANDOMDATA_SIZE,
"Copy failed");
Algorithm::Sort(temp);
Algorithm::Unique(temp);
Algorithm::LowerBounds(temp,handle);
Algorithm::UpperBounds(temp,handle1);
VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE,
"LowerBounds returned incorrect size");
std::copy(
vtkm::cont::ArrayPortalToIteratorBegin(handle.GetPortalConstControl()),
vtkm::cont::ArrayPortalToIteratorEnd(handle.GetPortalConstControl()),
randomData);
VTKM_TEST_ASSERT(randomData[0] == 2, "Got bad value - LowerBounds");
VTKM_TEST_ASSERT(randomData[1] == 3, "Got bad value - LowerBounds");
VTKM_TEST_ASSERT(randomData[2] == 3, "Got bad value - LowerBounds");
VTKM_TEST_ASSERT(randomData[3] == 0, "Got bad value - LowerBounds");
VTKM_TEST_ASSERT(randomData[4] == 1, "Got bad value - LowerBounds");
VTKM_TEST_ASSERT(randomData[5] == 3, "Got bad value - LowerBounds");
VTKM_TEST_ASSERT(handle1.GetNumberOfValues() == RANDOMDATA_SIZE,
"UppererBounds returned incorrect size");
std::copy(
vtkm::cont::ArrayPortalToIteratorBegin(handle1.GetPortalConstControl()),
vtkm::cont::ArrayPortalToIteratorEnd(handle1.GetPortalConstControl()),
randomData);
VTKM_TEST_ASSERT(randomData[0] == 3, "Got bad value - UpperBound");
VTKM_TEST_ASSERT(randomData[1] == 4, "Got bad value - UpperBound");
VTKM_TEST_ASSERT(randomData[2] == 4, "Got bad value - UpperBound");
VTKM_TEST_ASSERT(randomData[3] == 1, "Got bad value - UpperBound");
VTKM_TEST_ASSERT(randomData[4] == 2, "Got bad value - UpperBound");
VTKM_TEST_ASSERT(randomData[5] == 4, "Got bad value - UpperBound");
}
static VTKM_CONT_EXPORT void TestSort()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Sort" << std::endl;
vtkm::Id testData[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= OFFSET+((ARRAY_SIZE-i) % 50);
}
IdArrayHandle unsorted = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
IdArrayHandle sorted;
Algorithm::Copy(unsorted, sorted);
//Validate the standard inplace sort is correct
Algorithm::Sort(sorted);
for (vtkm::Id i = 0; i < ARRAY_SIZE-1; ++i)
{
vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i);
vtkm::Id sorted2 = sorted.GetPortalConstControl().Get(i+1);
VTKM_TEST_ASSERT(sorted1 <= sorted2, "Values not properly sorted.");
}
}
static VTKM_CONT_EXPORT void TestSortWithComparisonObject()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Sort with comparison object" << std::endl;
vtkm::Id testData[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= OFFSET+((ARRAY_SIZE-i) % 50);
}
//sort the users memory in-place
IdArrayHandle sorted = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
Algorithm::Sort(sorted);
//copy the sorted array into our own memory, if use the same user ptr
//we would also sort the 'sorted' handle
IdArrayHandle comp_sorted;
Algorithm::Copy(sorted, comp_sorted);
Algorithm::Sort(comp_sorted,vtkm::SortGreater());
//Validate that sorted and comp_sorted are sorted in the opposite directions
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i);
vtkm::Id sorted2 = comp_sorted.GetPortalConstControl().Get(ARRAY_SIZE - (i + 1));
VTKM_TEST_ASSERT(sorted1 == sorted2,
"Got bad sort values when using SortGreater");
}
//validate that sorted and comp_sorted are now equal
Algorithm::Sort(comp_sorted,vtkm::SortLess());
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i);
vtkm::Id sorted2 = comp_sorted.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(sorted1 == sorted2,
"Got bad sort values when using SortLess");
}
}
static VTKM_CONT_EXPORT void TestSortWithFancyArrays()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Sort of a ArrayHandleZip" << std::endl;
vtkm::Id testData[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= OFFSET+((ARRAY_SIZE-i) % 50);
}
IdArrayHandle unsorted = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
IdArrayHandle sorted;
Algorithm::Copy(unsorted, sorted);
//verify that we can use ArrayHandleZip inplace
vtkm::cont::ArrayHandleZip< IdArrayHandle, IdArrayHandle> zipped(unsorted, sorted);
//verify we can use sort with zip handle
Algorithm::Sort(zipped, vtkm::SortGreater());
Algorithm::Sort(zipped);
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
vtkm::Pair<vtkm::Id,vtkm::Id> kv_sorted = zipped.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(( OFFSET + ( i / (ARRAY_SIZE/50)) ) == kv_sorted.first,
"ArrayZipHandle improperly sorted");
}
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Sort of a ArrayHandlePermutation" << std::endl;
//verify that we can use ArrayHandlePermutation inplace
vtkm::cont::ArrayHandleIndex index(ARRAY_SIZE);
vtkm::cont::ArrayHandlePermutation< vtkm::cont::ArrayHandleIndex,
IdArrayHandle> perm(index, sorted);
//verify we can use a custom operator sort with permutation handle
Algorithm::Sort(perm, vtkm::SortGreater());
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
vtkm::Id sorted_value = perm.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(( OFFSET + ( (ARRAY_SIZE-(i+1)) / (ARRAY_SIZE/50)) ) == sorted_value,
"ArrayZipPermutation improperly sorted");
}
//verify we can use the default sort with permutation handle
Algorithm::Sort(perm);
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
vtkm::Id sorted_value = perm.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(( OFFSET + ( i / (ARRAY_SIZE/50)) ) == sorted_value,
"ArrayZipPermutation improperly sorted");
}
}
static VTKM_CONT_EXPORT void TestSortByKey()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Sort by keys" << std::endl;
typedef vtkm::Vec<FloatDefault,3> Vec3;
typedef vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::FloatDefault,3>,StorageTag>
Vec3ArrayHandle;
vtkm::Id testKeys[ARRAY_SIZE];
Vec3 testValues[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testKeys[i] = ARRAY_SIZE - i;
testValues[i] = TestValue(i, Vec3());
}
IdArrayHandle keys = vtkm::cont::make_ArrayHandle(testKeys, ARRAY_SIZE);
Vec3ArrayHandle values = vtkm::cont::make_ArrayHandle(testValues, ARRAY_SIZE);
Algorithm::SortByKey(keys,values);
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
//keys should be sorted from 1 to ARRAY_SIZE
//values should be sorted from (ARRAY_SIZE-1) to 0
Vec3 sorted_value = values.GetPortalConstControl().Get(i);
vtkm::Id sorted_key = keys.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT( (sorted_key == (i+1)) , "Got bad SortByKeys key");
VTKM_TEST_ASSERT( test_equal(sorted_value, TestValue(ARRAY_SIZE-1-i, Vec3())),
"Got bad SortByKeys value");
}
// this will return everything back to what it was before sorting
Algorithm::SortByKey(keys,values,vtkm::SortGreater());
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
//keys should be sorted from ARRAY_SIZE to 1
//values should be sorted from 0 to (ARRAY_SIZE-1)
Vec3 sorted_value = values.GetPortalConstControl().Get(i);
vtkm::Id sorted_key = keys.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT( (sorted_key == (ARRAY_SIZE-i)),
"Got bad SortByKeys key");
VTKM_TEST_ASSERT( test_equal(sorted_value, TestValue(i, Vec3())),
"Got bad SortByKeys value");
}
//this is here to verify we can sort by vtkm::Vec
Algorithm::SortByKey(values,keys);
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
//keys should be sorted from ARRAY_SIZE to 1
//values should be sorted from 0 to (ARRAY_SIZE-1)
Vec3 sorted_value = values.GetPortalConstControl().Get(i);
vtkm::Id sorted_key = keys.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT( (sorted_key == (ARRAY_SIZE-i)),
"Got bad SortByKeys key");
VTKM_TEST_ASSERT( test_equal(sorted_value, TestValue(i, Vec3())),
"Got bad SortByKeys value");
}
}
static VTKM_CONT_EXPORT void TestLowerBoundsWithComparisonObject()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Testing LowerBounds with comparison object" << std::endl;
vtkm::Id testData[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= OFFSET+(i % 50);
}
IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
//make a deep copy of input and place it into temp
IdArrayHandle temp;
Algorithm::Copy(input,temp);
Algorithm::Sort(temp);
Algorithm::Unique(temp);
IdArrayHandle handle;
//verify lower bounds work
Algorithm::LowerBounds(temp,input,handle,vtkm::SortLess());
// Check to make sure that temp was resized correctly during Unique.
// (This was a discovered bug at one point.)
temp.GetPortalConstControl(); // Forces copy back to control.
temp.ReleaseResourcesExecution(); // Make sure not counting on execution.
VTKM_TEST_ASSERT(
temp.GetNumberOfValues() == 50,
"Unique did not resize array (or size did not copy to control).");
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
vtkm::Id value = handle.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(value == i % 50, "Got bad LowerBounds value with SortLess");
}
}
static VTKM_CONT_EXPORT void TestUpperBoundsWithComparisonObject()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Testing UpperBounds with comparison object" << std::endl;
vtkm::Id testData[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= OFFSET+(i % 50);
}
IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
//make a deep copy of input and place it into temp
IdArrayHandle temp;
Algorithm::Copy(input,temp);
Algorithm::Sort(temp);
Algorithm::Unique(temp);
IdArrayHandle handle;
//verify upper bounds work
Algorithm::UpperBounds(temp,input,handle,vtkm::SortLess());
// Check to make sure that temp was resized correctly during Unique.
// (This was a discovered bug at one point.)
temp.GetPortalConstControl(); // Forces copy back to control.
temp.ReleaseResourcesExecution(); // Make sure not counting on execution.
VTKM_TEST_ASSERT(
temp.GetNumberOfValues() == 50,
"Unique did not resize array (or size did not copy to control).");
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
vtkm::Id value = handle.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(value == (i % 50)+1, "Got bad UpperBounds value with SortLess");
}
}
static VTKM_CONT_EXPORT void TestUniqueWithComparisonObject()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Testing Unique with comparison object" << std::endl;
vtkm::Id testData[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= OFFSET+(i % 50);
}
IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
Algorithm::Sort(input);
Algorithm::Unique(input, FuseAll());
// Check to make sure that input was resized correctly during Unique.
// (This was a discovered bug at one point.)
input.GetPortalConstControl(); // Forces copy back to control.
input.ReleaseResourcesExecution(); // Make sure not counting on execution.
VTKM_TEST_ASSERT(
input.GetNumberOfValues() == 1,
"Unique did not resize array (or size did not copy to control).");
vtkm::Id value = input.GetPortalConstControl().Get(0);
VTKM_TEST_ASSERT(value == OFFSET, "Got bad unique value");
}
static VTKM_CONT_EXPORT void TestReduce()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Reduce" << std::endl;
//construct the index array
IdArrayHandle array;
Algorithm::Schedule(
ClearArrayKernel(array.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
//the output of reduce and scan inclusive should be the same
vtkm::Id reduce_sum = Algorithm::Reduce(array, vtkm::Id(0));
vtkm::Id reduce_sum_with_intial_value = Algorithm::Reduce(array,
vtkm::Id(ARRAY_SIZE));
vtkm::Id inclusive_sum = Algorithm::ScanInclusive(array, array);
VTKM_TEST_ASSERT(reduce_sum == OFFSET * ARRAY_SIZE,
"Got bad sum from Reduce");
VTKM_TEST_ASSERT(reduce_sum_with_intial_value == reduce_sum + ARRAY_SIZE,
"Got bad sum from Reduce with initial value");
VTKM_TEST_ASSERT(reduce_sum == inclusive_sum,
"Got different sums from Reduce and ScanInclusive");
}
static VTKM_CONT_EXPORT void TestReduceWithComparisonObject()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Reduce with comparison object " << std::endl;
//construct the index array. Assign an abnormally large value
//to the middle of the array, that should be what we see as our sum.
vtkm::Id testData[ARRAY_SIZE];
const vtkm::Id maxValue = ARRAY_SIZE*2;
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= i;
}
testData[ARRAY_SIZE/2] = maxValue;
IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
vtkm::Id largestValue = Algorithm::Reduce(input,
vtkm::Id(),
comparison::MaxValue());
VTKM_TEST_ASSERT(largestValue == maxValue,
"Got bad value from Reduce with comparison object");
}
static VTKM_CONT_EXPORT void TestReduceWithFancyArrays()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Reduce with ArrayHandleZip" << std::endl;
{
IdArrayHandle keys, values;
Algorithm::Schedule(
ClearArrayKernel(keys.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
Algorithm::Schedule(
ClearArrayKernel(values.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
vtkm::cont::ArrayHandleZip< IdArrayHandle,
IdArrayHandle > zipped(keys,values);
//the output of reduce and scan inclusive should be the same
typedef vtkm::Pair<vtkm::Id,vtkm::Id> ResultType;
ResultType reduce_sum_with_intial_value =
Algorithm::Reduce(zipped, ResultType(ARRAY_SIZE,ARRAY_SIZE));
ResultType expectedResult(OFFSET * ARRAY_SIZE + ARRAY_SIZE,
OFFSET * ARRAY_SIZE + ARRAY_SIZE);
VTKM_TEST_ASSERT( ( reduce_sum_with_intial_value == expectedResult),
"Got bad sum from Reduce with initial value");
}
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Reduce with ArrayHandlePermutation" << std::endl;
{
//lastly test with heterogeneous zip values ( vec3, and constant array handle),
//and a custom reduce binary functor
const vtkm::Id indexLength = 30;
const vtkm::Id valuesLength = 10;
typedef vtkm::Float32 ValueType;
vtkm::Id indexs[indexLength] = {0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,
5,5,5,1,4,9,7,7,7,8,8,8,0,1,2};
ValueType values[valuesLength] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
6.0f, 7.0f, 8.0f, 9.0f, -2.0f};
const ValueType expectedSum = 125;
IdArrayHandle indexHandle = vtkm::cont::make_ArrayHandle(indexs, indexLength);
vtkm::cont::ArrayHandle<ValueType> valueHandle = vtkm::cont::make_ArrayHandle(values, valuesLength);
vtkm::cont::ArrayHandlePermutation< IdArrayHandle, vtkm::cont::ArrayHandle<ValueType> > perm;
perm = vtkm::cont::make_ArrayHandlePermutation(indexHandle, valueHandle);
const ValueType sum = Algorithm::Reduce(perm, ValueType(0.0f));
std::cout << "sum: " << sum << std::endl;
VTKM_TEST_ASSERT( ( sum == expectedSum),
"Got bad sum from Reduce with permutation handle");
}
}
static VTKM_CONT_EXPORT void TestReduceByKey()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Reduce By Key" << std::endl;
//first test with very basic integer key / values
{
const vtkm::Id inputLength = 12;
const vtkm::Id expectedLength = 6;
vtkm::Id inputKeys[inputLength] = {0, 0, 0,\
1, 1,\
4,\
0,\
2, 2, 2, 2,\
-1}; // input keys
vtkm::Id inputValues[inputLength] = {13, -2, -1,\
1, 1,\
0,\
3,\
1, 2, 3, 4, \
-42}; // input keys
vtkm::Id expectedKeys[expectedLength] = { 0, 1, 4, 0, 2, -1 };
vtkm::Id expectedValues[expectedLength] = {10, 2, 0, 3, 10, -42};
IdArrayHandle keys = vtkm::cont::make_ArrayHandle(inputKeys, inputLength);
IdArrayHandle values = vtkm::cont::make_ArrayHandle(inputValues, inputLength);
IdArrayHandle keysOut, valuesOut;
Algorithm::ReduceByKey( keys,
values,
keysOut,
valuesOut,
vtkm::internal::Add() );
VTKM_TEST_ASSERT(keysOut.GetNumberOfValues() == expectedLength,
"Got wrong number of output keys");
VTKM_TEST_ASSERT(valuesOut.GetNumberOfValues() == expectedLength,
"Got wrong number of output values");
for(vtkm::Id i=0; i < expectedLength; ++i)
{
const vtkm::Id k = keysOut.GetPortalConstControl().Get(i);
const vtkm::Id v = valuesOut.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT( expectedKeys[i] == k, "Incorrect reduced key");
VTKM_TEST_ASSERT( expectedValues[i] == v, "Incorrect reduced value");
}
}
//next test with a single key across the entire set, using vec3 as the
//value, using a custom reduce binary functor
{
const vtkm::Id inputLength = 3;
const vtkm::Id expectedLength = 1;
vtkm::Id inputKeys[inputLength] = {0, 0, 0}; // input keys
vtkm::Vec<vtkm::Float64, 3> inputValues[inputLength];
inputValues[0] = vtkm::make_Vec(13.1, 13.3, 13.5);
inputValues[1] = vtkm::make_Vec(-2.1, -2.3, -2.5);
inputValues[2] = vtkm::make_Vec(-1.0, -1.0, 1.0); // input keys
vtkm::Id expectedKeys[expectedLength] = { 0};
vtkm::Vec<vtkm::Float64, 3> expectedValues[expectedLength];
expectedValues[0] = vtkm::make_Vec(27.51, 30.59, -33.75);
IdArrayHandle keys = vtkm::cont::make_ArrayHandle(inputKeys, inputLength);
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float64, 3>, StorageTag> values = vtkm::cont::make_ArrayHandle(inputValues, inputLength);
IdArrayHandle keysOut;
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float64, 3>, StorageTag> valuesOut;
Algorithm::ReduceByKey( keys,
values,
keysOut,
valuesOut,
vtkm::internal::Multiply() );
VTKM_TEST_ASSERT(keysOut.GetNumberOfValues() == expectedLength,
"Got wrong number of output keys");
VTKM_TEST_ASSERT(valuesOut.GetNumberOfValues() == expectedLength,
"Got wrong number of output values");
for(vtkm::Id i=0; i < expectedLength; ++i)
{
const vtkm::Id k = keysOut.GetPortalConstControl().Get(i);
const vtkm::Vec<vtkm::Float64, 3> v = valuesOut.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT( expectedKeys[i] == k, "Incorrect reduced key");
VTKM_TEST_ASSERT( expectedValues[i] == v, "Incorrect reduced vale");
}
}
}
static VTKM_CONT_EXPORT void TestReduceByKeyWithFancyArrays()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Reduce By Key with Fancy Arrays" << std::endl;
//lastly test with heterogeneous zip values ( vec3, and constant array handle),
//and a custom reduce binary functor
const vtkm::Id inputLength = 30;
const vtkm::Id expectedLength = 10;
typedef vtkm::Float32 ValueType;
vtkm::Id inputKeys[inputLength] = {0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,
5,5,5,6,6,6,7,7,7,8,8,8,9,9,9}; // input keys
ValueType inputValues1[inputLength] = {13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f,
13.1f, -2.1f, -1.0f}; // input values array1
vtkm::Id expectedKeys[expectedLength] = {0,1,2,3,4,5,6,7,8,9};
ValueType expectedValues1[expectedLength] = {10.f,10.f,10.f,10.f,10.f,10.f,10.f,10.f,10.f,10.f}; // output values 1
ValueType expectedValues2[expectedLength] = {3.f,3.f,3.f,3.f,3.f,3.f,3.f,3.f,3.f,3.f}; // output values 2
IdArrayHandle keys = vtkm::cont::make_ArrayHandle(inputKeys, inputLength);
typedef vtkm::cont::ArrayHandle<ValueType, StorageTag> ValueArrayType;
ValueArrayType values1 = vtkm::cont::make_ArrayHandle(inputValues1, inputLength);
typedef vtkm::cont::ArrayHandleConstant<ValueType> ConstValueArrayType;
ConstValueArrayType constOneArray(1.f, inputLength);
vtkm::cont::ArrayHandleZip<ValueArrayType, ConstValueArrayType> valuesZip;
valuesZip = make_ArrayHandleZip(values1, constOneArray); // values in zip
IdArrayHandle keysOut;
ValueArrayType valuesOut1;
ValueArrayType valuesOut2;
vtkm::cont::ArrayHandleZip<ValueArrayType, ValueArrayType> valuesOutZip(valuesOut1, valuesOut2);
Algorithm::ReduceByKey( keys,
valuesZip,
keysOut,
valuesOutZip,
vtkm::internal::Add() );
VTKM_TEST_ASSERT(keysOut.GetNumberOfValues() == expectedLength,
"Got wrong number of output keys");
VTKM_TEST_ASSERT(valuesOutZip.GetNumberOfValues() == expectedLength,
"Got wrong number of output values");
for(vtkm::Id i=0; i < expectedLength; ++i)
{
const vtkm::Id k = keysOut.GetPortalConstControl().Get(i);
const vtkm::Pair<ValueType, ValueType> v = valuesOutZip.GetPortalConstControl().Get(i);
std::cout << "key=" << k << "," << "expectedValues1[i] = " << expectedValues1[i] << "," << "computed value1 = " << v.first << std::endl;
VTKM_TEST_ASSERT( expectedKeys[i] == k, "Incorrect reduced key");
VTKM_TEST_ASSERT( expectedValues1[i] == v.first, "Incorrect reduced value1");
VTKM_TEST_ASSERT( expectedValues2[i] == v.second, "Incorrect reduced value2");
}
}
static VTKM_CONT_EXPORT void TestScanInclusive()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Inclusive Scan" << std::endl;
{
//construct the index array
IdArrayHandle array;
Algorithm::Schedule(
ClearArrayKernel(array.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
//we know have an array whose sum is equal to OFFSET * ARRAY_SIZE,
//let's validate that
vtkm::Id sum = Algorithm::ScanInclusive(array, array);
VTKM_TEST_ASSERT(sum == OFFSET * ARRAY_SIZE,
"Got bad sum from Inclusive Scan");
//each value should be equal to the Triangle Number of that index
//ie 1, 3, 6, 10, 15, 21 ...
vtkm::Id partialSum = 0;
vtkm::Id triangleNumber = 0;
for(vtkm::Id i=0, pos=1; i < ARRAY_SIZE; ++i, ++pos)
{
const vtkm::Id value = array.GetPortalConstControl().Get(i);
partialSum += value;
triangleNumber = ((pos*(pos+1))/2);
VTKM_TEST_ASSERT(partialSum == triangleNumber * OFFSET,
"Incorrect partial sum");
}
}
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Inclusive Scan with multiplication operator" << std::endl;
{
vtkm::Float64 inputValues[ARRAY_SIZE];
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
inputValues[i] = 1.01;
}
vtkm::Id mid = ARRAY_SIZE/2;
inputValues[mid] = 0.0;
vtkm::cont::ArrayHandle<vtkm::Float64> array = vtkm::cont::make_ArrayHandle(inputValues,
ARRAY_SIZE);
vtkm::Float64 product = Algorithm::ScanInclusive(array, array,
vtkm::internal::Multiply());
VTKM_TEST_ASSERT(product == 0.0f, "ScanInclusive product result not 0.0");
for (vtkm::Id i = 0; i < mid; ++i)
{
vtkm::Float64 expected = pow(1.01, static_cast<vtkm::Float64>(i + 1));
vtkm::Float64 got = array.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(test_equal(got, expected),
"Incorrect results for ScanInclusive");
}
for (vtkm::Id i = mid; i < ARRAY_SIZE; ++i)
{
VTKM_TEST_ASSERT(array.GetPortalConstControl().Get(i) == 0.0f,
"Incorrect results for ScanInclusive");
}
}
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Inclusive Scan with a vtkm::Vec" << std::endl;
{
typedef vtkm::Vec<Float64,3> Vec3;
typedef vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float64,3>,StorageTag>
Vec3ArrayHandle;
Vec3 testValues[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testValues[i] = TestValue(i, Vec3());
}
Vec3ArrayHandle values = vtkm::cont::make_ArrayHandle(testValues, ARRAY_SIZE);
Vec3 sum = Algorithm::ScanInclusive(values, values);
std::cout << "Sum that was returned " << sum << std::endl;
VTKM_TEST_ASSERT( test_equal(sum, vtkm::make_Vec(6996.0,7996.0,8996.0) ),
"Got bad sum from Inclusive Scan");
}
}
static VTKM_CONT_EXPORT void TestScanInclusiveWithComparisonObject()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Inclusive Scan with comparison object " << std::endl;
//construct the index array
IdArrayHandle array;
Algorithm::Schedule(
ClearArrayKernel(array.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
Algorithm::Schedule(
AddArrayKernel(array.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
//we know have an array whose sum is equal to OFFSET * ARRAY_SIZE,
//let's validate that
IdArrayHandle result;
vtkm::Id sum = Algorithm::ScanInclusive(array,
result,
comparison::MaxValue());
VTKM_TEST_ASSERT(sum == OFFSET + (ARRAY_SIZE-1),
"Got bad sum from Inclusive Scan with comparison object");
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
const vtkm::Id input_value = array.GetPortalConstControl().Get(i);
const vtkm::Id result_value = result.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(input_value == result_value, "Incorrect partial sum");
}
//now try it inline
sum = Algorithm::ScanInclusive(array,
array,
comparison::MaxValue());
VTKM_TEST_ASSERT(sum == OFFSET + (ARRAY_SIZE-1),
"Got bad sum from Inclusive Scan with comparison object");
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
const vtkm::Id input_value = array.GetPortalConstControl().Get(i);
const vtkm::Id result_value = result.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(input_value == result_value, "Incorrect partial sum");
}
}
static VTKM_CONT_EXPORT void TestScanExclusive()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Exclusive Scan" << std::endl;
{
//construct the index array
IdArrayHandle array;
Algorithm::Schedule(
ClearArrayKernel(array.PrepareForOutput(ARRAY_SIZE,
DeviceAdapterTag())),
ARRAY_SIZE);
// we know have an array whose sum = (OFFSET * ARRAY_SIZE),
// let's validate that
vtkm::Id sum = Algorithm::ScanExclusive(array, array);
std::cout << "Sum that was returned " << sum << std::endl;
VTKM_TEST_ASSERT(sum == (OFFSET * ARRAY_SIZE),
"Got bad sum from Exclusive Scan");
//each value should be equal to the Triangle Number of that index
//ie 0, 1, 3, 6, 10, 15, 21 ...
vtkm::Id partialSum = 0;
vtkm::Id triangleNumber = 0;
for(vtkm::Id i=0, pos=0; i < ARRAY_SIZE; ++i, ++pos)
{
const vtkm::Id value = array.GetPortalConstControl().Get(i);
partialSum += value;
triangleNumber = ((pos*(pos+1))/2);
VTKM_TEST_ASSERT(partialSum == triangleNumber * OFFSET,
"Incorrect partial sum");
}
}
// Enable when Exclusive Scan with custom operator is implemented for all
// device adaptors
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Exclusive Scan with multiplication operator" << std::endl;
{
vtkm::Float64 inputValues[ARRAY_SIZE];
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
inputValues[i] = 1.01;
}
vtkm::Id mid = ARRAY_SIZE/2;
inputValues[mid] = 0.0;
vtkm::cont::ArrayHandle<vtkm::Float64> array = vtkm::cont::make_ArrayHandle(inputValues,
ARRAY_SIZE);
vtkm::Float64 initialValue = 2.00;
vtkm::Float64 product = Algorithm::ScanExclusive(array, array,
vtkm::internal::Multiply(), initialValue);
VTKM_TEST_ASSERT(product == 0.0f, "ScanExclusive product result not 0.0");
VTKM_TEST_ASSERT(array.GetPortalConstControl().Get(0) == initialValue,
"ScanExclusive result's first value != initialValue");
for (vtkm::Id i = 1; i <= mid; ++i)
{
vtkm::Float64 expected = pow(1.01, static_cast<vtkm::Float64>(i)) *
initialValue;
vtkm::Float64 got = array.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(test_equal(got, expected),
"Incorrect results for ScanExclusive");
}
for (vtkm::Id i = mid + 1; i < ARRAY_SIZE; ++i)
{
VTKM_TEST_ASSERT(array.GetPortalConstControl().Get(i) == 0.0f,
"Incorrect results for ScanExclusive");
}
}
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Exclusive Scan with a vtkm::Vec" << std::endl;
{
typedef vtkm::Vec<Float64,3> Vec3;
typedef vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float64,3>,StorageTag>
Vec3ArrayHandle;
Vec3 testValues[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testValues[i] = TestValue(i, Vec3());
}
Vec3ArrayHandle values = vtkm::cont::make_ArrayHandle(testValues, ARRAY_SIZE);
Vec3 sum = Algorithm::ScanExclusive(values, values);
std::cout << "Sum that was returned " << sum << std::endl;
VTKM_TEST_ASSERT( test_equal(sum, vtkm::make_Vec(6996.0,7996.0,8996.0) ),
"Got bad sum from Exclusive Scan");
}
}
static VTKM_CONT_EXPORT void TestErrorExecution()
{
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing Exceptions in Execution Environment" << std::endl;
std::cout << "Generating one error." << std::endl;
std::string message;
try
{
Algorithm::Schedule(OneErrorKernel(), ARRAY_SIZE);
}
catch (vtkm::cont::ErrorExecution error)
{
std::cout << "Got expected error: " << error.GetMessage() << std::endl;
message = error.GetMessage();
}
VTKM_TEST_ASSERT(message == ERROR_MESSAGE,
"Did not get expected error message.");
std::cout << "Generating lots of errors." << std::endl;
message = "";
try
{
Algorithm::Schedule(AllErrorKernel(), ARRAY_SIZE);
}
catch (vtkm::cont::ErrorExecution error)
{
std::cout << "Got expected error: " << error.GetMessage() << std::endl;
message = error.GetMessage();
}
VTKM_TEST_ASSERT(message == ERROR_MESSAGE,
"Did not get expected error message.");
}
static VTKM_CONT_EXPORT void TestCopyArraysInDiffTypes()
{
std::cout << "-------------------------------------------------" << std::endl;
std::cout << "Testing Copy to a different array type" << std::endl;
vtkm::Id testData[ARRAY_SIZE];
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
testData[i]= OFFSET+(i % 50);
}
IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData, ARRAY_SIZE);
//make a deep copy of input and place it into temp
vtkm::cont::ArrayHandle<vtkm::Float64> temp;
Algorithm::Copy(input,temp);
for(vtkm::Id i=0; i < ARRAY_SIZE; ++i)
{
vtkm::Float64 value = temp.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(value == testData[i], "Got bad value (Copy)");
}
}
static VTKM_CONT_EXPORT void TestAtomicArray()
{
vtkm::Int32 atomicCount = 0;
for(vtkm::Int32 i = 0; i < ARRAY_SIZE; i++) atomicCount += i;
std::cout << "-------------------------------------------" << std::endl;
// To test the atomics, ARRAY_SIZE number of threads will all increment
// a single atomic value.
std::cout << "Testing Atomic Add with vtkm::Int32" << std::endl;
{
std::vector<vtkm::Int32> singleElement;
singleElement.push_back(0);
vtkm::cont::ArrayHandle<vtkm::Int32> atomicElement = vtkm::cont::make_ArrayHandle(singleElement);
vtkm::exec::AtomicArray<vtkm::Int32, DeviceAdapterTag> atomic(atomicElement);
Algorithm::Schedule(AtomicKernel<vtkm::Int32>(atomic), ARRAY_SIZE);
vtkm::Int32 expected = vtkm::Int32(atomicCount);
vtkm::Int32 actual= atomicElement.GetPortalControl().Get(0);
VTKM_TEST_ASSERT(expected == actual, "Did not get expected value: Atomic add Int32");
}
std::cout << "Testing Atomic Add with vtkm::Int64" << std::endl;
{
std::vector<vtkm::Int64> singleElement;
singleElement.push_back(0);
vtkm::cont::ArrayHandle<vtkm::Int64> atomicElement = vtkm::cont::make_ArrayHandle(singleElement);
vtkm::exec::AtomicArray<vtkm::Int64, DeviceAdapterTag> atomic(atomicElement);
Algorithm::Schedule(AtomicKernel<vtkm::Int64>(atomic), ARRAY_SIZE);
vtkm::Int64 expected = vtkm::Int64(atomicCount);
vtkm::Int64 actual= atomicElement.GetPortalControl().Get(0);
VTKM_TEST_ASSERT(expected == actual, "Did not get expected value: Atomic add Int64");
}
std::cout << "Testing Atomic CAS with vtkm::Int32" << std::endl;
{
std::vector<vtkm::Int32> singleElement;
singleElement.push_back(0);
vtkm::cont::ArrayHandle<vtkm::Int32> atomicElement = vtkm::cont::make_ArrayHandle(singleElement);
vtkm::exec::AtomicArray<vtkm::Int32, DeviceAdapterTag> atomic(atomicElement);
Algorithm::Schedule(AtomicCASKernel<vtkm::Int32>(atomic), ARRAY_SIZE);
vtkm::Int32 expected = vtkm::Int32(atomicCount);
vtkm::Int32 actual= atomicElement.GetPortalControl().Get(0);
VTKM_TEST_ASSERT(expected == actual, "Did not get expected value: Atomic CAS Int32");
}
std::cout << "Testing Atomic CAS with vtkm::Int64" << std::endl;
{
std::vector<vtkm::Int64> singleElement;
singleElement.push_back(0);
vtkm::cont::ArrayHandle<vtkm::Int64> atomicElement = vtkm::cont::make_ArrayHandle(singleElement);
vtkm::exec::AtomicArray<vtkm::Int64, DeviceAdapterTag> atomic(atomicElement);
Algorithm::Schedule(AtomicCASKernel<vtkm::Int64>(atomic), ARRAY_SIZE);
vtkm::Int64 expected = vtkm::Int64(atomicCount);
vtkm::Int64 actual= atomicElement.GetPortalControl().Get(0);
VTKM_TEST_ASSERT(expected == actual, "Did not get expected value: Atomic CAS Int64");
}
}
struct TestAll
{
VTKM_CONT_EXPORT void operator()() const
{
std::cout << "Doing DeviceAdapter tests" << std::endl;
TestArrayManagerExecution();
TestOutOfMemory();
TestTimer();
TestRuntime();
TestAlgorithmSchedule();
TestErrorExecution();
TestReduce();
TestReduceWithComparisonObject();
TestReduceWithFancyArrays();
TestReduceByKey();
TestReduceByKeyWithFancyArrays();
TestScanExclusive();
TestScanInclusive();
TestScanInclusiveWithComparisonObject();
TestSort();
TestSortWithComparisonObject();
TestSortWithFancyArrays();
TestSortByKey();
TestLowerBoundsWithComparisonObject();
TestUpperBoundsWithComparisonObject();
TestUniqueWithComparisonObject();
TestOrderedUniqueValues(); //tests Copy, LowerBounds, Sort, Unique
TestStreamCompactWithStencil();
TestStreamCompact();
TestCopyArraysInDiffTypes();
TestAtomicArray();
}
};
public:
/// Run a suite of tests to check to see if a DeviceAdapter properly supports
/// all members and classes required for driving vtkm algorithms. Returns an
/// error code that can be returned from the main function of a test.
///
static VTKM_CONT_EXPORT int Run()
{
return vtkm::cont::testing::Testing::Run(TestAll());
}
};
#undef ERROR_MESSAGE
#undef ARRAY_SIZE
#undef OFFSET
#undef DIM
}
}
} // namespace vtkm::cont::testing
#endif //vtk_m_cont_testing_TestingDeviceAdapter_h