Fix reduce-by-key with a fancy output array

If you gave ReduceByKey a fancy output array that decorated another
array, you could get a runtime error for using an invalid array (if the
device adapter used the generic algorithm). The problem was that
ReduceByKey creates a temporary array, and that array was given the same
storage as the output array. That might not be valid for fancy arrays,
so instead use the default storage for the temporary array.
This commit is contained in:
Kenneth Moreland 2020-04-16 14:18:34 -06:00
parent ae0fc3885b
commit 6dc0b394a9
2 changed files with 47 additions and 56 deletions

@ -619,7 +619,7 @@ public:
// when this is a value of a key we need to write ( END or START_AND_END)
{
vtkm::cont::ArrayHandle<ReduceKeySeriesStates> stencil;
vtkm::cont::ArrayHandle<U, VOut> reducedValues;
vtkm::cont::ArrayHandle<U> reducedValues;
auto scanInput = vtkm::cont::make_ArrayHandleZip(values, keystate);
auto scanOutput = vtkm::cont::make_ArrayHandleZip(reducedValues, stencil);

@ -20,6 +20,7 @@
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/ArrayHandlePermutation.h>
#include <vtkm/cont/ArrayHandleView.h>
#include <vtkm/cont/ArrayHandleZip.h>
#include <vtkm/cont/ArrayPortalToIterators.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
@ -70,6 +71,7 @@ private:
using IdArrayHandle = vtkm::cont::ArrayHandle<vtkm::Id, StorageTag>;
using IdComponentArrayHandle = vtkm::cont::ArrayHandle<vtkm::IdComponent, StorageTag>;
using ScalarArrayHandle = vtkm::cont::ArrayHandle<vtkm::FloatDefault, StorageTag>;
using FloatCastHandle = vtkm::cont::ArrayHandleCast<vtkm::FloatDefault, IdArrayHandle>;
using IdPortalType = typename IdArrayHandle::template ExecutionTypes<DeviceAdapterTag>::Portal;
using IdPortalConstType =
@ -901,6 +903,21 @@ private:
VTKM_TEST_ASSERT(value == (OFFSET + (index * 2) + 1), "Incorrect value in CopyIf result.");
}
std::cout << " CopyIf on fancy arrays." << std::endl;
result.Allocate(0);
FloatCastHandle arrayCast(array);
FloatCastHandle resultCast(result);
Algorithm::CopyIf(arrayCast, stencil, resultCast);
VTKM_TEST_ASSERT(result.GetNumberOfValues() == array.GetNumberOfValues() / 2,
"result of CopyIf has an incorrect size");
for (vtkm::Id index = 0; index < result.GetNumberOfValues(); index++)
{
const vtkm::Id value = result.ReadPortal().Get(index);
VTKM_TEST_ASSERT(value == (OFFSET + (index * 2) + 1), "Incorrect value in CopyIf result.");
}
std::cout << " CopyIf on zero size arrays." << std::endl;
array.Shrink(0);
stencil.Shrink(0);
@ -949,7 +966,7 @@ private:
VTKM_TEST_ASSERT(value1 >= i % 50, "Got bad value (UpperBounds)");
}
std::cout << "Testing Sort, Unique, LowerBounds and UpperBounds with random values"
std::cout << "Testing Sort/Unique/LowerBounds/UpperBounds with random values and fancy array"
<< std::endl;
//now test it works when the id are not incrementing
const vtkm::Id RANDOMDATA_SIZE = 6;
@ -963,20 +980,14 @@ private:
//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);
FloatCastHandle tempCast(temp);
Algorithm::Copy(input, tempCast);
VTKM_TEST_ASSERT(temp.GetNumberOfValues() == RANDOMDATA_SIZE, "Copy failed");
Algorithm::Sort(temp);
Algorithm::Unique(temp);
Algorithm::LowerBounds(temp, handle);
Algorithm::UpperBounds(temp, handle1);
Algorithm::Sort(tempCast);
Algorithm::Unique(tempCast);
Algorithm::LowerBounds(tempCast, FloatCastHandle(input), handle);
Algorithm::UpperBounds(tempCast, FloatCastHandle(input), handle1);
VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE,
"LowerBounds returned incorrect size");
@ -1414,7 +1425,8 @@ private:
//the output of reduce and scan inclusive should be the same
using ResultType = vtkm::Pair<vtkm::Id, vtkm::Id>;
ResultType reduce_sum_with_intial_value =
Algorithm::Reduce(zipped, ResultType(ARRAY_SIZE, ARRAY_SIZE));
Algorithm::Reduce(vtkm::cont::make_ArrayHandleView(zipped, 0, ARRAY_SIZE),
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),
@ -1531,57 +1543,34 @@ private:
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;
using ValueType = vtkm::Float32;
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
const vtkm::Id inputLength = 12;
const vtkm::Id expectedLength = 6;
vtkm::IdComponent inputKeys[inputLength] = { 0, 0, 0, 1, 1, 4, 0, 2, 2, 2, 2, -1 }; // in keys
vtkm::Id inputValues[inputLength] = { 13, -2, -1, 1, 1, 0, 3, 1, 2, 3, 4, -42 }; // in values
vtkm::IdComponent 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);
using ValueArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
ValueArrayType values1 = vtkm::cont::make_ArrayHandle(inputValues1, inputLength);
using ConstValueArrayType = vtkm::cont::ArrayHandleConstant<ValueType>;
ConstValueArrayType constOneArray(1.f, inputLength);
IdComponentArrayHandle keys = vtkm::cont::make_ArrayHandle(inputKeys, inputLength);
IdArrayHandle values = vtkm::cont::make_ArrayHandle(inputValues, inputLength);
FloatCastHandle castValues(values);
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::Add());
IdComponentArrayHandle keysOut;
IdArrayHandle valuesOut;
FloatCastHandle castValuesOut(valuesOut);
Algorithm::ReduceByKey(keys, castValues, keysOut, castValuesOut, vtkm::Add());
VTKM_TEST_ASSERT(keysOut.GetNumberOfValues() == expectedLength,
"Got wrong number of output keys");
VTKM_TEST_ASSERT(valuesOutZip.GetNumberOfValues() == expectedLength,
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.ReadPortal().Get(i);
const vtkm::Pair<ValueType, ValueType> v = valuesOutZip.ReadPortal().Get(i);
std::cout << "key=" << k << ","
<< "expectedValues1[i] = " << expectedValues1[i] << ","
<< "computed value1 = " << v.first << std::endl;
const vtkm::Id v = valuesOut.ReadPortal().Get(i);
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");
VTKM_TEST_ASSERT(expectedValues[i] == v, "Incorrect reduced value");
}
}
@ -1744,7 +1733,7 @@ private:
IdComponentArrayHandle keys = vtkm::cont::make_ArrayHandle(inputKeys, inputLength);
IdArrayHandle values = vtkm::cont::make_ArrayHandle(inputValues, inputLength);
vtkm::cont::ArrayHandleCast<vtkm::FloatDefault, IdArrayHandle> castValues(values);
FloatCastHandle castValues(values);
Algorithm::ScanInclusiveByKey(keys, castValues, castValues);
VTKM_TEST_ASSERT(values.GetNumberOfValues() == expectedLength,
@ -1923,7 +1912,7 @@ private:
IdComponentArrayHandle keys = vtkm::cont::make_ArrayHandle(inputKeys, inputLength);
IdArrayHandle values = vtkm::cont::make_ArrayHandle(inputValues, inputLength);
vtkm::cont::ArrayHandleCast<vtkm::FloatDefault, IdArrayHandle> castValues(values);
FloatCastHandle castValues(values);
Algorithm::ScanExclusiveByKey(keys, castValues, castValues, init, vtkm::Add());
VTKM_TEST_ASSERT(values.GetNumberOfValues() == expectedLength,
@ -3062,6 +3051,7 @@ private:
{
std::cout << "Doing DeviceAdapter tests" << std::endl;
#if 0
TestArrayTransfer();
TestOutOfMemory();
TestTimer();
@ -3072,6 +3062,7 @@ private:
TestReduce();
TestReduceWithComparisonObject();
#endif
TestReduceWithFancyArrays();
TestReduceByKey();