//============================================================================ // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace vtkm { namespace cont { namespace testing { #define ERROR_MESSAGE "Got an error." #define ARRAY_SIZE 100000 #define OFFSET 1000 #define DIM_SIZE 128 /// This class has a single static member, Run, that tests the templated /// DeviceAdapter for conformance. /// template struct TestingDeviceAdapter { private: typedef vtkm::cont::StorageTagBasic StorageTag; typedef vtkm::cont::ArrayHandle IdArrayHandle; typedef vtkm::cont::ArrayHandle ScalarArrayHandle; typedef vtkm::cont::internal::ArrayManagerExecution< vtkm::Id, StorageTag, DeviceAdapterTag> IdArrayManagerExecution; typedef vtkm::cont::internal::Storage IdStorage; typedef typename IdArrayHandle::template ExecutionTypes ::Portal IdPortalType; typedef typename IdArrayHandle::template ExecutionTypes ::PortalConst IdPortalConstType; typedef vtkm::cont::DeviceAdapterAlgorithm Algorithm; public: // Cuda kernels have to be public (in Cuda 4.0). struct CopyArrayKernel { VTKM_CONT CopyArrayKernel(const IdPortalConstType &input, const IdPortalType &output) : InputArray(input), OutputArray(output) { } VTKM_EXEC void operator()( vtkm::Id index, const vtkm::exec::internal::ErrorMessageBuffer &) const { this->OutputArray.Set(index, this->InputArray.Get(index)); } VTKM_CONT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalConstType InputArray; IdPortalType OutputArray; }; struct ClearArrayKernel { VTKM_CONT ClearArrayKernel(const IdPortalType &array) : Array(array), Dims() { } VTKM_CONT ClearArrayKernel(const IdPortalType &array, const vtkm::Id3& dims) : Array(array), Dims(dims) { } VTKM_EXEC void operator()(vtkm::Id index) const { this->Array.Set(index, OFFSET); } VTKM_EXEC 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 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 VTKM_EXEC void operator()(T& value) const { value = OFFSET; } }; struct AddArrayKernel { VTKM_CONT AddArrayKernel(const IdPortalType &array) : Array(array), Dims() { } VTKM_CONT AddArrayKernel(const IdPortalType &array, const vtkm::Id3& dims) : Array(array), Dims(dims) { } VTKM_EXEC void operator()(vtkm::Id index) const { this->Array.Set(index, this->Array.Get(index) + index); } VTKM_EXEC 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 void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalType Array; vtkm::Id3 Dims; }; struct OneErrorKernel { VTKM_EXEC void operator()(vtkm::Id index) const { if (index == ARRAY_SIZE/2) { this->ErrorMessage.RaiseError(ERROR_MESSAGE); } } VTKM_CONT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &errorMessage) { this->ErrorMessage = errorMessage; } vtkm::exec::internal::ErrorMessageBuffer ErrorMessage; }; struct AllErrorKernel { VTKM_EXEC void operator()(vtkm::Id vtkmNotUsed(index)) const { this->ErrorMessage.RaiseError(ERROR_MESSAGE); } VTKM_CONT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &errorMessage) { this->ErrorMessage = errorMessage; } vtkm::exec::internal::ErrorMessageBuffer ErrorMessage; }; struct OffsetPlusIndexKernel { VTKM_CONT OffsetPlusIndexKernel(const IdPortalType &array) : Array(array) { } VTKM_EXEC void operator()(vtkm::Id index) const { this->Array.Set(index, OFFSET + index); } VTKM_CONT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalType Array; }; struct MarkOddNumbersKernel { VTKM_CONT MarkOddNumbersKernel(const IdPortalType &array) : Array(array) { } VTKM_EXEC void operator()(vtkm::Id index) const { this->Array.Set(index, index%2); } VTKM_CONT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalType Array; }; struct FuseAll { template VTKM_EXEC bool operator()(const T&, const T&) const { //binary predicates for unique return true if they are the same return true; } }; template struct AtomicKernel { VTKM_CONT AtomicKernel(const vtkm::exec::AtomicArray &array) : AArray(array) { } VTKM_EXEC void operator()(vtkm::Id index) const { T value = (T) index; this->AArray.Add(0, value); } VTKM_CONT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } vtkm::exec::AtomicArray AArray; }; template struct AtomicCASKernel { VTKM_CONT AtomicCASKernel(const vtkm::exec::AtomicArray &array) : AArray(array) { } VTKM_EXEC 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 void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } vtkm::exec::AtomicArray AArray; }; private: static VTKM_CONT void TestDeviceAdapterTag() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing device adapter tag" << std::endl; typedef vtkm::cont::DeviceAdapterTraits 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 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 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 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, StorageTagBasic> supportArray; vtkm::cont::internal::ArrayManagerExecution< vtkm::Vec, 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 static void TestTimer() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Timer" << std::endl; vtkm::cont::Timer 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 static void TestRuntime() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing RuntimeDeviceInformation" << std::endl; vtkm::cont::RuntimeDeviceInformation runtime; const bool valid_runtime = runtime.Exists(); VTKM_TEST_ASSERT(valid_runtime, "runtime detection failed for device"); } static VTKM_CONT 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+=100) { 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 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 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 void TestOrderedUniqueValues() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing Sort, Unique, LowerBounds and UpperBounds" << std::endl; std::vector testData(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testData[i]= static_cast(OFFSET+(i % 50)); } IdArrayHandle input = vtkm::cont::make_ArrayHandle(&(*testData.begin()), 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 void TestSort() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Sort" << std::endl; std::vector testData(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testData[i]= static_cast(OFFSET+((ARRAY_SIZE-i) % 50)); } IdArrayHandle unsorted = vtkm::cont::make_ArrayHandle(testData); 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 void TestSortWithComparisonObject() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Sort with comparison object" << std::endl; std::vector testData(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testData[i]= static_cast(OFFSET+((ARRAY_SIZE-i) % 50)); } //sort the users memory in-place IdArrayHandle sorted = vtkm::cont::make_ArrayHandle(testData); 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 void TestSortWithFancyArrays() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Sort of a ArrayHandleZip" << std::endl; std::vector testData(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testData[i]= static_cast(OFFSET+((ARRAY_SIZE-i) % 50)); } IdArrayHandle unsorted = vtkm::cont::make_ArrayHandle(testData); 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 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 void TestSortByKey() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Sort by keys" << std::endl; typedef vtkm::Vec Vec3; typedef vtkm::cont::ArrayHandle,StorageTag> Vec3ArrayHandle; std::vector testKeys(ARRAY_SIZE); std::vector testValues(testKeys.size()); for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { std::size_t index = static_cast(i); testKeys[index] = ARRAY_SIZE - i; testValues[index] = TestValue(i, Vec3()); } IdArrayHandle keys = vtkm::cont::make_ArrayHandle(testKeys); Vec3ArrayHandle values = vtkm::cont::make_ArrayHandle(testValues); 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 void TestLowerBoundsWithComparisonObject() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing LowerBounds with comparison object" << std::endl; std::vector testData(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testData[i]= static_cast(OFFSET+(i % 50)); } IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData); //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 void TestUpperBoundsWithComparisonObject() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing UpperBounds with comparison object" << std::endl; std::vector testData(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testData[i]= static_cast(OFFSET+(i % 50)); } IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData); //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 void TestUniqueWithComparisonObject() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing Unique with comparison object" << std::endl; std::vector testData(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testData[i]= static_cast(OFFSET+(i % 50)); } IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData); 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 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 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. std::vector testData(ARRAY_SIZE); const vtkm::Id maxValue = ARRAY_SIZE*2; for(std::size_t i=0; i < ARRAY_SIZE; ++i) { vtkm::Id index = static_cast(i); testData[i]= index; } testData[ARRAY_SIZE/2] = maxValue; IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData); vtkm::Vec range = Algorithm::Reduce(input, vtkm::Vec(0,0), vtkm::MinAndMax()); VTKM_TEST_ASSERT(maxValue == range[1], "Got bad value from Reduce with comparison object"); VTKM_TEST_ASSERT(0 == range[0], "Got bad value from Reduce with comparison object"); } static VTKM_CONT 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 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 valueHandle = vtkm::cont::make_ArrayHandle(values, valuesLength); vtkm::cont::ArrayHandlePermutation< IdArrayHandle, vtkm::cont::ArrayHandle > 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 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::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 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 expectedValues[expectedLength]; expectedValues[0] = vtkm::make_Vec(27.51, 30.59, -33.75); IdArrayHandle keys = vtkm::cont::make_ArrayHandle(inputKeys, inputLength); vtkm::cont::ArrayHandle, StorageTag> values = vtkm::cont::make_ArrayHandle(inputValues, inputLength); IdArrayHandle keysOut; vtkm::cont::ArrayHandle, StorageTag> valuesOut; Algorithm::ReduceByKey( keys, values, keysOut, valuesOut, vtkm::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 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 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 ValueArrayType; ValueArrayType values1 = vtkm::cont::make_ArrayHandle(inputValues1, inputLength); typedef vtkm::cont::ArrayHandleConstant ConstValueArrayType; ConstValueArrayType constOneArray(1.f, inputLength); vtkm::cont::ArrayHandleZip valuesZip; valuesZip = make_ArrayHandleZip(values1, constOneArray); // values in zip IdArrayHandle keysOut; ValueArrayType valuesOut1; ValueArrayType valuesOut2; vtkm::cont::ArrayHandleZip valuesOutZip(valuesOut1, valuesOut2); Algorithm::ReduceByKey( keys, valuesZip, keysOut, valuesOutZip, vtkm::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 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 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; { std::vector inputValues(ARRAY_SIZE); for (std::size_t i = 0; i < ARRAY_SIZE; ++i) { inputValues[i] = 1.01; } std::size_t mid = ARRAY_SIZE/2; inputValues[mid] = 0.0; vtkm::cont::ArrayHandle array = vtkm::cont::make_ArrayHandle(&inputValues[0], ARRAY_SIZE); vtkm::Float64 product = Algorithm::ScanInclusive(array, array, vtkm::Multiply()); VTKM_TEST_ASSERT(product == 0.0f, "ScanInclusive product result not 0.0"); for (std::size_t i = 0; i < mid; ++i) { vtkm::Id index = static_cast(i); vtkm::Float64 expected = pow(1.01, static_cast(i + 1)); vtkm::Float64 got = array.GetPortalConstControl().Get(index); VTKM_TEST_ASSERT(test_equal(got, expected), "Incorrect results for ScanInclusive"); } for (std::size_t i = mid; i < ARRAY_SIZE; ++i) { vtkm::Id index = static_cast(i); VTKM_TEST_ASSERT(array.GetPortalConstControl().Get(index) == 0.0f, "Incorrect results for ScanInclusive"); } } std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Inclusive Scan with a vtkm::Vec" << std::endl; { typedef vtkm::Vec Vec3; typedef vtkm::cont::ArrayHandle,StorageTag> Vec3ArrayHandle; std::vector testValues(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testValues[i] = TestValue(1, Vec3()); } Vec3ArrayHandle values = vtkm::cont::make_ArrayHandle(testValues); Vec3 sum = Algorithm::ScanInclusive(values, values); std::cout << "Sum that was returned " << sum << std::endl; VTKM_TEST_ASSERT( test_equal(sum, TestValue(1, Vec3()) * ARRAY_SIZE ), "Got bad sum from Inclusive Scan"); } } static VTKM_CONT 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, vtkm::Maximum()); 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, vtkm::Maximum()); 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 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; { std::vector inputValues(ARRAY_SIZE); for (std::size_t i = 0; i < ARRAY_SIZE; ++i) { inputValues[i] = 1.01; } std::size_t mid = ARRAY_SIZE/2; inputValues[mid] = 0.0; vtkm::cont::ArrayHandle array = vtkm::cont::make_ArrayHandle(inputValues); vtkm::Float64 initialValue = 2.00; vtkm::Float64 product = Algorithm::ScanExclusive(array, array, vtkm::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 (std::size_t i = 1; i <= mid; ++i) { vtkm::Id index = static_cast(i); vtkm::Float64 expected = pow(1.01, static_cast(i)) * initialValue; vtkm::Float64 got = array.GetPortalConstControl().Get(index); VTKM_TEST_ASSERT(test_equal(got, expected), "Incorrect results for ScanExclusive"); } for (std::size_t i = mid + 1; i < ARRAY_SIZE; ++i) { vtkm::Id index = static_cast(i); VTKM_TEST_ASSERT(array.GetPortalConstControl().Get(index) == 0.0f, "Incorrect results for ScanExclusive"); } } std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Exclusive Scan with a vtkm::Vec" << std::endl; { typedef vtkm::Vec Vec3; typedef vtkm::cont::ArrayHandle,StorageTag> Vec3ArrayHandle; std::vector testValues(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testValues[i] = TestValue(1, Vec3()); } Vec3ArrayHandle values = vtkm::cont::make_ArrayHandle(testValues); Vec3 sum = Algorithm::ScanExclusive(values, values); std::cout << "Sum that was returned " << sum << std::endl; VTKM_TEST_ASSERT( test_equal(sum, (TestValue(1, Vec3()) * ARRAY_SIZE) ), "Got bad sum from Exclusive Scan"); } } static VTKM_CONT 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."); } template struct TestCopy {}; template struct TestCopy { static T get(vtkm::Id i) { return static_cast(i); } }; template struct TestCopy > { static vtkm::Vec get(vtkm::Id i) { vtkm::Vec temp; for (int j=0; j( OFFSET+(i % 50) ); } return temp; } }; template static VTKM_CONT void TestCopyArrays() { #define COPY_ARRAY_SIZE 10000 std::vector testData(COPY_ARRAY_SIZE); vtkm::Id index = 0; for(std::size_t i=0; i < COPY_ARRAY_SIZE; ++i, ++index) { testData[i]= TestCopy::get(index); } vtkm::cont::ArrayHandle input = vtkm::cont::make_ArrayHandle(&testData[0], COPY_ARRAY_SIZE); //make a deep copy of input and place it into temp { vtkm::cont::ArrayHandle temp; temp.Allocate( COPY_ARRAY_SIZE * 2 ); Algorithm::Copy(input,temp); VTKM_TEST_ASSERT(temp.GetNumberOfValues() == COPY_ARRAY_SIZE, "Copy Needs to Resize Array"); typename std::vector::const_iterator c = testData.begin(); for(vtkm::Id i=0; i < COPY_ARRAY_SIZE; i+=50,c+=50) { T value = temp.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(value == *c, "Got bad value (Copy)"); } } //CopySubRange tests: //1. Verify invalid input start position fails { vtkm::cont::ArrayHandle output; bool result = Algorithm::CopySubRange(input, COPY_ARRAY_SIZE*4, 1, output); VTKM_TEST_ASSERT(result == false, "CopySubRange when given bad input offset"); } //2. Verify unallocated output gets allocated { vtkm::cont::ArrayHandle output; bool result = Algorithm::CopySubRange(input, 0, COPY_ARRAY_SIZE, output); VTKM_TEST_ASSERT(result == true, "CopySubRange should succeed"); VTKM_TEST_ASSERT(output.GetNumberOfValues() == COPY_ARRAY_SIZE, "CopySubRange needs to allocate output"); } //3. Verify under allocated output gets resized properly { vtkm::cont::ArrayHandle output; output.Allocate(2); bool result = Algorithm::CopySubRange(input, 0, COPY_ARRAY_SIZE, output); VTKM_TEST_ASSERT(result == true, "CopySubRange should succeed"); VTKM_TEST_ASSERT(output.GetNumberOfValues() == COPY_ARRAY_SIZE, "CopySubRange needs to re-allocate output"); } //4. Verify invalid input length gets shortened { vtkm::cont::ArrayHandle output; bool result = Algorithm::CopySubRange(input, 100, COPY_ARRAY_SIZE, output); VTKM_TEST_ASSERT(result == true, "CopySubRange needs to shorten input range"); VTKM_TEST_ASSERT(output.GetNumberOfValues() == (COPY_ARRAY_SIZE-100), "CopySubRange needs to shorten input range"); typename std::vector::const_iterator c = testData.begin() + 100; for(vtkm::Id i=0; i < (COPY_ARRAY_SIZE-100); i+=100,c+=100) { T value = output.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(value == *c, "Got bad value (CopySubRange 2)"); } } //5. Verify sub range copy works when copying into a larger output { vtkm::cont::ArrayHandle output; output.Allocate( COPY_ARRAY_SIZE * 2 ); Algorithm::CopySubRange(input, 0, COPY_ARRAY_SIZE, output); Algorithm::CopySubRange(input, 0, COPY_ARRAY_SIZE, output, COPY_ARRAY_SIZE); VTKM_TEST_ASSERT(output.GetNumberOfValues() == (COPY_ARRAY_SIZE*2), "CopySubRange needs to not resize array"); typename std::vector::const_iterator c = testData.begin(); for(vtkm::Id i=0; i < COPY_ARRAY_SIZE; i+=50,c+=50) { T value = output.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(value == *c, "Got bad value (CopySubRange 5)"); value = output.GetPortalConstControl().Get(COPY_ARRAY_SIZE + i); VTKM_TEST_ASSERT(value == *c, "Got bad value (CopySubRange 5)"); } } //6. Verify that whey sub range needs to reallocate the output it // properly copies the original data instead of clearing it { vtkm::cont::ArrayHandle output; output.Allocate( COPY_ARRAY_SIZE ); Algorithm::CopySubRange(input, 0, COPY_ARRAY_SIZE, output); Algorithm::CopySubRange(input, 0, COPY_ARRAY_SIZE, output, COPY_ARRAY_SIZE); VTKM_TEST_ASSERT(output.GetNumberOfValues() == (COPY_ARRAY_SIZE*2), "CopySubRange needs too resize Array"); typename std::vector::const_iterator c = testData.begin(); for(vtkm::Id i=0; i < COPY_ARRAY_SIZE; i+=50,c+=50) { T value = output.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(value == *c, "Got bad value (CopySubRange 6)"); value = output.GetPortalConstControl().Get(COPY_ARRAY_SIZE + i); VTKM_TEST_ASSERT(value == *c, "Got bad value (CopySubRange 6)"); } } { vtkm::cont::ArrayHandle output; //7. Verify negative input index returns false bool result = Algorithm::CopySubRange(input, -1, COPY_ARRAY_SIZE, output); VTKM_TEST_ASSERT(result == false, "CopySubRange negative index should fail"); //8. Verify negative input numberOfElementsToCopy returns false result = Algorithm::CopySubRange(input, 0, -COPY_ARRAY_SIZE, output); VTKM_TEST_ASSERT(result == false, "CopySubRange negative number elements should fail"); //9. Verify negative output index return false result = Algorithm::CopySubRange(input, 0, COPY_ARRAY_SIZE, output, -2); VTKM_TEST_ASSERT(result == false, "CopySubRange negative output index should fail"); } #undef COPY_ARRAY_SIZE } static VTKM_CONT void TestCopyArraysMany() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing Copy to same array type" << std::endl; TestCopyArrays< vtkm::Vec >(); TestCopyArrays< vtkm::Vec >(); // TestCopyArrays< vtkm::Vec >(); TestCopyArrays< vtkm::Vec >(); TestCopyArrays< vtkm::Vec >(); TestCopyArrays< vtkm::Vec >(); // TestCopyArrays< vtkm::Float32 >(); TestCopyArrays< vtkm::Float64 >(); // TestCopyArrays< vtkm::Int8 >(); TestCopyArrays< vtkm::Int16 >(); TestCopyArrays< vtkm::Int32 >(); TestCopyArrays< vtkm::Int64 >(); // TestCopyArrays< vtkm::UInt8 >(); TestCopyArrays< vtkm::UInt16 >(); TestCopyArrays< vtkm::UInt32 >(); TestCopyArrays< vtkm::UInt64 >(); // TestCopyArrays(); } static VTKM_CONT void TestCopyArraysInDiffTypes() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing Copy to a different array type" << std::endl; std::vector testData(ARRAY_SIZE); for(std::size_t i=0; i < ARRAY_SIZE; ++i) { testData[i]= static_cast(OFFSET+(i % 50)); } IdArrayHandle input = vtkm::cont::make_ArrayHandle(testData); //make a deep copy of input and place it into temp vtkm::cont::ArrayHandle temp; Algorithm::Copy(input,temp); std::vector::const_iterator c = testData.begin(); for(vtkm::Id i=0; i < ARRAY_SIZE; ++i, ++c) { vtkm::Float64 value = temp.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(value == static_cast(*c), "Got bad value (Copy)"); } } static VTKM_CONT void TestAtomicArray() { //we can't use ARRAY_SIZE as that would cause a overflow vtkm::Int32 SHORT_ARRAY_SIZE = 10000; vtkm::Int32 atomicCount = 0; for(vtkm::Int32 i = 0; i < SHORT_ARRAY_SIZE; i++) atomicCount += i; std::cout << "-------------------------------------------" << std::endl; // To test the atomics, SHORT_ARRAY_SIZE number of threads will all increment // a single atomic value. std::cout << "Testing Atomic Add with vtkm::Int32" << std::endl; { std::vector singleElement; singleElement.push_back(0); vtkm::cont::ArrayHandle atomicElement = vtkm::cont::make_ArrayHandle(singleElement); vtkm::exec::AtomicArray atomic(atomicElement); Algorithm::Schedule(AtomicKernel(atomic), SHORT_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 singleElement; singleElement.push_back(0); vtkm::cont::ArrayHandle atomicElement = vtkm::cont::make_ArrayHandle(singleElement); vtkm::exec::AtomicArray atomic(atomicElement); Algorithm::Schedule(AtomicKernel(atomic), SHORT_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 singleElement; singleElement.push_back(0); vtkm::cont::ArrayHandle atomicElement = vtkm::cont::make_ArrayHandle(singleElement); vtkm::exec::AtomicArray atomic(atomicElement); Algorithm::Schedule(AtomicCASKernel(atomic), SHORT_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 singleElement; singleElement.push_back(0); vtkm::cont::ArrayHandle atomicElement = vtkm::cont::make_ArrayHandle(singleElement); vtkm::exec::AtomicArray atomic(atomicElement); Algorithm::Schedule(AtomicCASKernel(atomic), SHORT_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 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(); TestCopyArraysMany(); 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 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