2017-11-29 15:44:37 +00:00
|
|
|
//============================================================================
|
|
|
|
// Copyright (c) Kitware, Inc.
|
|
|
|
// All rights reserved.
|
|
|
|
// See LICENSE.txt for details.
|
2019-04-15 23:24:21 +00:00
|
|
|
//
|
2017-11-29 15:44:37 +00:00
|
|
|
// 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.
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
#include <vtkm/cont/Algorithm.h>
|
|
|
|
|
|
|
|
#include <vtkm/TypeTraits.h>
|
|
|
|
|
|
|
|
#include <vtkm/cont/testing/Testing.h>
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
// The goal of this unit test is not to verify the correctness
|
|
|
|
// of the various algorithms. Since Algorithm is a header, we
|
2018-08-07 21:50:41 +00:00
|
|
|
// need to ensure we instantiate each algorithm in a source
|
2017-11-29 15:44:37 +00:00
|
|
|
// file to verify compilation.
|
|
|
|
//
|
2018-02-27 14:25:25 +00:00
|
|
|
static constexpr vtkm::Id ARRAY_SIZE = 10;
|
2017-11-29 15:44:37 +00:00
|
|
|
|
2019-10-09 21:39:49 +00:00
|
|
|
void FillTest()
|
|
|
|
{
|
|
|
|
vtkm::cont::BitField bits;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> array;
|
|
|
|
|
|
|
|
bits.Allocate(ARRAY_SIZE);
|
|
|
|
array.Allocate(ARRAY_SIZE);
|
|
|
|
|
|
|
|
vtkm::cont::Algorithm::Fill(bits, true);
|
|
|
|
vtkm::cont::Algorithm::Fill(bits, true, 5);
|
|
|
|
vtkm::cont::Algorithm::Fill(bits, vtkm::UInt8(0xab));
|
|
|
|
vtkm::cont::Algorithm::Fill(bits, vtkm::UInt8(0xab), 5);
|
|
|
|
vtkm::cont::Algorithm::Fill(array, vtkm::Id(5));
|
|
|
|
vtkm::cont::Algorithm::Fill(array, vtkm::Id(5), 5);
|
|
|
|
}
|
|
|
|
|
2017-11-29 15:44:37 +00:00
|
|
|
void CopyTest()
|
|
|
|
{
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> input;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> output;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> stencil;
|
|
|
|
|
|
|
|
input.Allocate(ARRAY_SIZE);
|
|
|
|
output.Allocate(ARRAY_SIZE);
|
|
|
|
stencil.Allocate(ARRAY_SIZE);
|
|
|
|
|
|
|
|
vtkm::cont::Algorithm::Copy(input, output);
|
|
|
|
vtkm::cont::Algorithm::CopyIf(input, stencil, output);
|
|
|
|
vtkm::cont::Algorithm::CopyIf(input, stencil, output, vtkm::LogicalNot());
|
|
|
|
vtkm::cont::Algorithm::CopySubRange(input, 2, 1, output);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BoundsTest()
|
|
|
|
{
|
|
|
|
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> input;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> output;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> values;
|
|
|
|
|
|
|
|
input.Allocate(ARRAY_SIZE);
|
|
|
|
output.Allocate(ARRAY_SIZE);
|
|
|
|
values.Allocate(ARRAY_SIZE);
|
|
|
|
|
|
|
|
vtkm::cont::Algorithm::LowerBounds(input, values, output);
|
|
|
|
vtkm::cont::Algorithm::LowerBounds(input, values, output, vtkm::Sum());
|
|
|
|
vtkm::cont::Algorithm::LowerBounds(input, values);
|
|
|
|
|
|
|
|
vtkm::cont::Algorithm::UpperBounds(input, values, output);
|
|
|
|
vtkm::cont::Algorithm::UpperBounds(input, values, output, vtkm::Sum());
|
|
|
|
vtkm::cont::Algorithm::UpperBounds(input, values);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReduceTest()
|
|
|
|
{
|
|
|
|
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> input;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> keys;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> keysOut;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> valsOut;
|
|
|
|
|
|
|
|
input.Allocate(ARRAY_SIZE);
|
|
|
|
keys.Allocate(ARRAY_SIZE);
|
|
|
|
keysOut.Allocate(ARRAY_SIZE);
|
|
|
|
valsOut.Allocate(ARRAY_SIZE);
|
|
|
|
|
|
|
|
vtkm::Id result;
|
|
|
|
result = vtkm::cont::Algorithm::Reduce(input, vtkm::Id(0));
|
|
|
|
result = vtkm::cont::Algorithm::Reduce(input, vtkm::Id(0), vtkm::Maximum());
|
|
|
|
vtkm::cont::Algorithm::ReduceByKey(keys, input, keysOut, valsOut, vtkm::Maximum());
|
|
|
|
(void)result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScanTest()
|
|
|
|
{
|
|
|
|
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> input;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> output;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> keys;
|
|
|
|
|
|
|
|
input.Allocate(ARRAY_SIZE);
|
|
|
|
output.Allocate(ARRAY_SIZE);
|
|
|
|
keys.Allocate(ARRAY_SIZE);
|
|
|
|
|
|
|
|
vtkm::Id out;
|
|
|
|
out = vtkm::cont::Algorithm::ScanInclusive(input, output);
|
|
|
|
out = vtkm::cont::Algorithm::ScanInclusive(input, output, vtkm::Maximum());
|
|
|
|
vtkm::cont::Algorithm::ScanInclusiveByKey(keys, input, output, vtkm::Maximum());
|
|
|
|
vtkm::cont::Algorithm::ScanInclusiveByKey(keys, input, output);
|
|
|
|
out = vtkm::cont::Algorithm::ScanExclusive(input, output, vtkm::Maximum(), vtkm::Id(0));
|
|
|
|
vtkm::cont::Algorithm::ScanExclusiveByKey(keys, input, output, vtkm::Id(0), vtkm::Maximum());
|
|
|
|
vtkm::cont::Algorithm::ScanExclusiveByKey(keys, input, output);
|
2019-09-03 15:53:14 +00:00
|
|
|
vtkm::cont::Algorithm::ScanExtended(input, output);
|
|
|
|
vtkm::cont::Algorithm::ScanExtended(input, output, vtkm::Maximum(), vtkm::Id(0));
|
2017-11-29 15:44:37 +00:00
|
|
|
(void)out;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DummyFunctor : public vtkm::exec::FunctorBase
|
|
|
|
{
|
|
|
|
template <typename IdType>
|
|
|
|
VTKM_EXEC void operator()(IdType) const
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void ScheduleTest()
|
|
|
|
{
|
|
|
|
vtkm::cont::Algorithm::Schedule(DummyFunctor(), vtkm::Id(1));
|
|
|
|
vtkm::Id3 id3(1, 1, 1);
|
|
|
|
vtkm::cont::Algorithm::Schedule(DummyFunctor(), id3);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct CompFunctor
|
|
|
|
{
|
|
|
|
template <typename T>
|
|
|
|
VTKM_EXEC_CONT bool operator()(const T& x, const T& y) const
|
|
|
|
{
|
|
|
|
return x < y;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
Support ExecArg behavior in vtkm::cont::Algorithm methods
Most of the arguments given to device adapter algorithms are actually
control-side arguments that get converted to execution objects internally
(usually a `vtkm::cont::ArrayHandle`). However, some of the algorithms,
take an argument that is passed directly to the execution environment, such
as the predicate argument of `Sort`. If the argument is a plain-old-data
(POD) type, which is common enough, then you can just pass the object
straight through. However, if the object has any special elements that have
to be transferred to the execution environment, such as internal arrays,
passing this to the `vtkm::cont::Algorithm` functions becomes
problematic.
To cover this use case, all the `vtkm::cont::Algorithm` functions now
support automatically transferring objects that support the `ExecObject`
worklet convention. If any argument to any of the `vtkm::cont::Algorithm`
functions inherits from `vtkm::cont::ExecutionObjectBase`, then the
`PrepareForExecution` method is called with the device the algorithm is
running on, which allows these device-specific objects to be used without
the hassle of creating a `TryExecute`.
2018-07-06 07:50:01 +00:00
|
|
|
struct CompExecObject : vtkm::cont::ExecutionObjectBase
|
|
|
|
{
|
|
|
|
template <typename Device>
|
2020-01-21 20:18:03 +00:00
|
|
|
VTKM_CONT CompFunctor PrepareForExecution(Device, vtkm::cont::Token&)
|
Support ExecArg behavior in vtkm::cont::Algorithm methods
Most of the arguments given to device adapter algorithms are actually
control-side arguments that get converted to execution objects internally
(usually a `vtkm::cont::ArrayHandle`). However, some of the algorithms,
take an argument that is passed directly to the execution environment, such
as the predicate argument of `Sort`. If the argument is a plain-old-data
(POD) type, which is common enough, then you can just pass the object
straight through. However, if the object has any special elements that have
to be transferred to the execution environment, such as internal arrays,
passing this to the `vtkm::cont::Algorithm` functions becomes
problematic.
To cover this use case, all the `vtkm::cont::Algorithm` functions now
support automatically transferring objects that support the `ExecObject`
worklet convention. If any argument to any of the `vtkm::cont::Algorithm`
functions inherits from `vtkm::cont::ExecutionObjectBase`, then the
`PrepareForExecution` method is called with the device the algorithm is
running on, which allows these device-specific objects to be used without
the hassle of creating a `TryExecute`.
2018-07-06 07:50:01 +00:00
|
|
|
{
|
|
|
|
return CompFunctor();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-11-29 15:44:37 +00:00
|
|
|
void SortTest()
|
|
|
|
{
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> input;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> keys;
|
|
|
|
|
|
|
|
input.Allocate(ARRAY_SIZE);
|
|
|
|
keys.Allocate(ARRAY_SIZE);
|
|
|
|
|
|
|
|
vtkm::cont::Algorithm::Sort(input);
|
|
|
|
vtkm::cont::Algorithm::Sort(input, CompFunctor());
|
Support ExecArg behavior in vtkm::cont::Algorithm methods
Most of the arguments given to device adapter algorithms are actually
control-side arguments that get converted to execution objects internally
(usually a `vtkm::cont::ArrayHandle`). However, some of the algorithms,
take an argument that is passed directly to the execution environment, such
as the predicate argument of `Sort`. If the argument is a plain-old-data
(POD) type, which is common enough, then you can just pass the object
straight through. However, if the object has any special elements that have
to be transferred to the execution environment, such as internal arrays,
passing this to the `vtkm::cont::Algorithm` functions becomes
problematic.
To cover this use case, all the `vtkm::cont::Algorithm` functions now
support automatically transferring objects that support the `ExecObject`
worklet convention. If any argument to any of the `vtkm::cont::Algorithm`
functions inherits from `vtkm::cont::ExecutionObjectBase`, then the
`PrepareForExecution` method is called with the device the algorithm is
running on, which allows these device-specific objects to be used without
the hassle of creating a `TryExecute`.
2018-07-06 07:50:01 +00:00
|
|
|
vtkm::cont::Algorithm::Sort(input, CompExecObject());
|
2017-11-29 15:44:37 +00:00
|
|
|
vtkm::cont::Algorithm::SortByKey(keys, input);
|
|
|
|
vtkm::cont::Algorithm::SortByKey(keys, input, CompFunctor());
|
Support ExecArg behavior in vtkm::cont::Algorithm methods
Most of the arguments given to device adapter algorithms are actually
control-side arguments that get converted to execution objects internally
(usually a `vtkm::cont::ArrayHandle`). However, some of the algorithms,
take an argument that is passed directly to the execution environment, such
as the predicate argument of `Sort`. If the argument is a plain-old-data
(POD) type, which is common enough, then you can just pass the object
straight through. However, if the object has any special elements that have
to be transferred to the execution environment, such as internal arrays,
passing this to the `vtkm::cont::Algorithm` functions becomes
problematic.
To cover this use case, all the `vtkm::cont::Algorithm` functions now
support automatically transferring objects that support the `ExecObject`
worklet convention. If any argument to any of the `vtkm::cont::Algorithm`
functions inherits from `vtkm::cont::ExecutionObjectBase`, then the
`PrepareForExecution` method is called with the device the algorithm is
running on, which allows these device-specific objects to be used without
the hassle of creating a `TryExecute`.
2018-07-06 07:50:01 +00:00
|
|
|
vtkm::cont::Algorithm::SortByKey(keys, input, CompExecObject());
|
2017-11-29 15:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SynchronizeTest()
|
|
|
|
{
|
|
|
|
vtkm::cont::Algorithm::Synchronize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void UniqueTest()
|
|
|
|
{
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Id> input;
|
|
|
|
|
|
|
|
input.Allocate(ARRAY_SIZE);
|
|
|
|
|
|
|
|
vtkm::cont::Algorithm::Unique(input);
|
|
|
|
vtkm::cont::Algorithm::Unique(input, CompFunctor());
|
Support ExecArg behavior in vtkm::cont::Algorithm methods
Most of the arguments given to device adapter algorithms are actually
control-side arguments that get converted to execution objects internally
(usually a `vtkm::cont::ArrayHandle`). However, some of the algorithms,
take an argument that is passed directly to the execution environment, such
as the predicate argument of `Sort`. If the argument is a plain-old-data
(POD) type, which is common enough, then you can just pass the object
straight through. However, if the object has any special elements that have
to be transferred to the execution environment, such as internal arrays,
passing this to the `vtkm::cont::Algorithm` functions becomes
problematic.
To cover this use case, all the `vtkm::cont::Algorithm` functions now
support automatically transferring objects that support the `ExecObject`
worklet convention. If any argument to any of the `vtkm::cont::Algorithm`
functions inherits from `vtkm::cont::ExecutionObjectBase`, then the
`PrepareForExecution` method is called with the device the algorithm is
running on, which allows these device-specific objects to be used without
the hassle of creating a `TryExecute`.
2018-07-06 07:50:01 +00:00
|
|
|
vtkm::cont::Algorithm::Unique(input, CompExecObject());
|
2017-11-29 15:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestAll()
|
|
|
|
{
|
2019-10-09 21:39:49 +00:00
|
|
|
FillTest();
|
2017-11-29 15:44:37 +00:00
|
|
|
CopyTest();
|
|
|
|
BoundsTest();
|
|
|
|
ReduceTest();
|
|
|
|
ScanTest();
|
|
|
|
ScheduleTest();
|
|
|
|
SortTest();
|
|
|
|
SynchronizeTest();
|
|
|
|
UniqueTest();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
2019-01-01 22:19:02 +00:00
|
|
|
int UnitTestAlgorithm(int argc, char* argv[])
|
2017-11-29 15:44:37 +00:00
|
|
|
{
|
2019-01-01 22:19:02 +00:00
|
|
|
return vtkm::cont::testing::Testing::Run(TestAll, argc, argv);
|
2017-11-29 15:44:37 +00:00
|
|
|
}
|