4.15. Device Algorithms

As described in Chapter 3.1 (General Approach), Viskores is built around the concept of a device adapter that encapsulates the necessary features of each device on which Viskores can run. At the core of the device adapter is a collection of basic algorithms optimized for the specific device. Many features of Viskores, such as worklets, are built on top of these device algorithms. Using these higher-level structures simplifies programming.

However, it is sometimes desirable to run these algorithms directly. Viskores comes with the viskores::cont::Algorithm class that provides a set of algorithms that can be invoked in the control environment and are run in the execution environment. All algorithms also accept an optional device adapter argument.

struct Algorithm

Static control-side entry points for common device algorithms.

Algorithm contains no state. It provides static methods that are invoked in the control environment and execute on a device adapter. Overloads that take a DeviceAdapterId use that device; overloads without an explicit device use the runtime device selection mechanism.

Unless otherwise stated, output ArrayHandle objects are resized or allocated by these methods as needed.

The following sections document the available methods.

Did You Know?

Many of the following device adapter algorithms take input and output viskores::cont::ArrayHandle objects, and these functions will handle their own memory management. This means that it is unnecessary to allocate output arrays. For example, it is unnecessary to call viskores::cont::ArrayHandle::Allocate() for the output array passed to the viskores::cont::Algorithm::Copy() method.

4.15.1. BitFieldToUnorderedSet

template<typename IndicesStorage>
static inline viskores::Id viskores::cont::Algorithm::BitFieldToUnorderedSet(viskores::cont::DeviceAdapterId devId, const viskores::cont::BitField &bits, viskores::cont::ArrayHandle<Id, IndicesStorage> &indices)

Create a unique, unordered list of set bit indices.

Writes into indices the indices of all bits set in bits. The resulting indices are unique, but no ordering is guaranteed.

Parameters:
  • devId – Device adapter to run on.

  • bits – Bit field to search.

  • indices – Output array that receives the set bit indices.

Returns:

The number of set bits, which is also the number of values written to indices.

template<typename IndicesStorage>
static inline viskores::Id viskores::cont::Algorithm::BitFieldToUnorderedSet(const viskores::cont::BitField &bits, viskores::cont::ArrayHandle<Id, IndicesStorage> &indices)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

The viskores::cont::Algorithm::BitFieldToUnorderedSet() method creates a unique, unsorted list of indices denoting which bits are set in a bit field. For example, running viskores::cont::Algorithm::BitFieldToUnorderedSet() on an input of [0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1] would return an array containing [2, 4, 6, 7, 9, 10, 11] or those numbers in some other order.

Example 4.149 Using the BitFieldToUnorderedSet algorithm.
1    viskores::cont::BitField bits;
2    bits.Allocate(32);
3
4    auto fillPortal = bits.WritePortal();
5    fillPortal.SetWord(0, viskores::UInt32(0xaa770011));
6
7    viskores::cont::ArrayHandle<viskores::Id> output;
8    auto setBits = viskores::cont::Algorithm::BitFieldToUnorderedSet(bits, output);

4.15.2. Copy

template<typename T, typename U, class CIn, class COut>
static inline bool viskores::cont::Algorithm::Copy(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<U, COut> &output)

Copy values from one array to another in the execution environment.

Copies all values from input to output. The output array is resized to the same number of values as input. If output is already allocated, its previous contents are discarded.

Parameters:
  • devId – Device adapter to run on.

  • input – Source array.

  • output – Destination array.

Returns:

true if the copy was successfully executed on the selected device.

template<typename T, typename U, class CIn, class COut>
static inline void viskores::cont::Algorithm::Copy(const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<U, COut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses any available device.

When possible, this overload prefers a device where input is already valid.

The viskores::cont::Algorithm::Copy() method copies data from an input array to an output array. The copy takes place in the execution environment.

Example 4.150 Using the Copy algorithm.
1    viskores::cont::ArrayHandleIndex input(12);
2
3    viskores::cont::ArrayHandle<viskores::Int32> output;
4
5    viskores::cont::Algorithm::Copy(input, output);
6
7    // output has { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }

4.15.3. CopyIf

template<typename T, typename U, class CIn, class CStencil, class COut>
static inline void viskores::cont::Algorithm::CopyIf(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<U, CStencil> &stencil, viskores::cont::ArrayHandle<T, COut> &output)

Conditionally copy input values selected by a stencil.

Performs stream compaction on input. For each position, the corresponding value in stencil determines whether that input value is copied to output. Without an explicit predicate, stencil values not equal to viskores::TypeTraits<U>::ZeroInitialization() are considered true. The output array is resized to the number of selected values, and selected input values are written in input order.

Parameters:
  • devId – Device adapter to run on.

  • input – Values to compact.

  • stencil – Stencil values. Must have the same number of values as input.

  • output – Destination array for selected values.

template<typename T, typename U, class CIn, class CStencil, class COut>
static inline void viskores::cont::Algorithm::CopyIf(const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<U, CStencil> &stencil, viskores::cont::ArrayHandle<T, COut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, typename U, class CIn, class CStencil, class COut, class UnaryPredicate>
static inline void viskores::cont::Algorithm::CopyIf(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<U, CStencil> &stencil, viskores::cont::ArrayHandle<T, COut> &output, UnaryPredicate unary_predicate)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses unary_predicate to decide which stencil values are true.

template<typename T, typename U, class CIn, class CStencil, class COut, class UnaryPredicate>
static inline void viskores::cont::Algorithm::CopyIf(const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<U, CStencil> &stencil, viskores::cont::ArrayHandle<T, COut> &output, UnaryPredicate unary_predicate)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses unary_predicate and the runtime device selection mechanism.

The viskores::cont::Algorithm::CopyIf() method selectively removes values from an array. The copy if algorithm is also sometimes referred to as stream compact. The first argument, the input, is an viskores::cont::ArrayHandle to be compacted by removing elements. The second argument, the stencil, is an viskores::cont::ArrayHandle of equal size with flags indicating whether the corresponding input value is to be copied to the output. The third argument is an output viskores::cont::ArrayHandle whose length is set to the number of true flags in the stencil, and the passed values are put in order in the output array.

viskores::cont::Algorithm::CopyIf() also accepts an optional fourth argument that is a unary predicate to determine what values in the stencil, the second argument, should be considered true. See Section 4.15.23 (Predicates and Operators) for more information on unary predicates. The unary predicate determines the true or false value of the stencil that determines whether a given entry is copied. If no unary predicate is given, then viskores::cont::Algorithm::CopyIf() will copy all values whose stencil value is not equal to 0 or the closest equivalent to it. More specifically, it copies values not equal to viskores::TypeTraits::ZeroInitialization().

Example 4.151 Using the CopyIf algorithm.
 1  struct LessThan5
 2  {
 3    VISKORES_EXEC_CONT bool operator()(viskores::Int32 x) const { return x < 5; }
 4  };
 5
 6
 7    viskores::cont::ArrayHandle<viskores::Int32> input =
 8      viskores::cont::make_ArrayHandle<viskores::Int32>(
 9        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
10    viskores::cont::ArrayHandle<viskores::UInt8> stencil =
11      viskores::cont::make_ArrayHandle<viskores::UInt8>(
12        { 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1 });
13
14    viskores::cont::ArrayHandle<viskores::Int32> output;
15
16    viskores::cont::Algorithm::CopyIf(input, stencil, output);
17
18    // output has { 0, 5, 3, 8, 3 }
19
20    viskores::cont::Algorithm::CopyIf(input, input, output, LessThan5());
21
22    // output has { 0, 1, 1, 4, 3, 3 }

4.15.4. CopySubRange

template<typename T, typename U, class CIn, class COut>
static inline bool viskores::cont::Algorithm::CopySubRange(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, viskores::Id inputStartIndex, viskores::Id numberOfElementsToCopy, viskores::cont::ArrayHandle<U, COut> &output, viskores::Id outputIndex = 0)

Copy a subrange from one array into another.

Copies up to numberOfElementsToCopy values from input, beginning at inputStartIndex, into output beginning at outputIndex. If the requested input range extends past the end of input, only the values up to the end of input are copied. If output is not large enough, it is resized.

Parameters:
  • devId – Device adapter to run on.

  • input – Source array.

  • inputStartIndex – First source index to copy.

  • numberOfElementsToCopyMaximum number of values to copy.

  • output – Destination array.

  • outputIndex – First destination index to write.

Pre:

If input and output share memory, the source and destination ranges must not overlap.

Returns:

true if the requested copy range is valid for the selected device implementation.

template<typename T, typename U, class CIn, class COut>
static inline bool viskores::cont::Algorithm::CopySubRange(const viskores::cont::ArrayHandle<T, CIn> &input, viskores::Id inputStartIndex, viskores::Id numberOfElementsToCopy, viskores::cont::ArrayHandle<U, COut> &output, viskores::Id outputIndex = 0)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

The viskores::cont::Algorithm::CopySubRange() method copies the contents of a section of one viskores::cont::ArrayHandle to another. The first argument is the input viskores::cont::ArrayHandle. The second argument is the index from which to start copying data. The third argument is the number of values to copy from the input to the output. The fourth argument is the output viskores::cont::ArrayHandle, which will be grown if it is not large enough. The fifth argument, which is optional, is the index in the output array to start copying data to. If the output index is not specified, data are copied to the beginning of the output array.

Example 4.152 Using the CopySubRange algorithm.
1    viskores::cont::ArrayHandle<viskores::Int32> input =
2      viskores::cont::make_ArrayHandle<viskores::Int32>(
3        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
4
5    viskores::cont::ArrayHandle<viskores::Int32> output;
6
7    viskores::cont::Algorithm::CopySubRange(input, 1, 7, output);
8
9    // output has { 0, 1, 1, 5, 5, 4, 3 }

4.15.5. CountSetBits

static inline viskores::Id viskores::cont::Algorithm::CountSetBits(viskores::cont::DeviceAdapterId devId, const viskores::cont::BitField &bits)

Count the total number of set bits in a bit field.

Parameters:
  • devId – Device adapter to run on.

  • bits – Bit field to inspect.

Returns:

The number of bits set to 1 in bits.

static inline viskores::Id viskores::cont::Algorithm::CountSetBits(const viskores::cont::BitField &bits)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

The viskores::cont::Algorithm::CountSetBits() method returns the total number of set bits in a viskores::cont::BitField. For example, running viskores::cont::Algorithm::CountSetBits() on an input of [0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1] would return 7.

Example 4.153 Using the CountSetBits algorithm.
 1    viskores::cont::BitField bits;
 2    bits.Allocate(32);
 3
 4    auto fillPortal = bits.WritePortal();
 5    fillPortal.SetWord(0, viskores::UInt32(0xaa770011));
 6
 7    viskores::cont::ArrayHandle<viskores::Id> output;
 8    auto setBits = viskores::cont::Algorithm::CountSetBits(bits);
 9
10    // Will return that there are 12 set bits

4.15.6. Fill

static inline void viskores::cont::Algorithm::Fill(viskores::cont::DeviceAdapterId devId, viskores::cont::BitField &bits, bool value, viskores::Id numBits)

Fill a bit field with a boolean value.

All bits are set to 1 when value is true and 0 when value is false. The bit field is resized to numBits before it is filled.

Parameters:
  • devId – Device adapter to run on.

  • bits – Bit field to fill.

  • value – Boolean value to stamp across the bit field.

  • numBits – Number of bits in the resized bit field.

static inline void viskores::cont::Algorithm::Fill(viskores::cont::BitField &bits, bool value, viskores::Id numBits)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

static inline void viskores::cont::Algorithm::Fill(viskores::cont::DeviceAdapterId devId, viskores::cont::BitField &bits, bool value)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Fills the existing bit field size.

static inline void viskores::cont::Algorithm::Fill(viskores::cont::BitField &bits, bool value)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Fills the existing bit field size using the runtime device selection mechanism.

template<typename WordType>
static inline void viskores::cont::Algorithm::Fill(viskores::cont::DeviceAdapterId devId, viskores::cont::BitField &bits, WordType word, viskores::Id numBits)

Fill a bit field by stamping a word mask across it.

The bit field is resized to numBits before it is filled.

Parameters:
  • devId – Device adapter to run on.

  • bits – Bit field to fill.

  • word – Word mask to stamp across the bit field.

  • numBits – Number of bits in the resized bit field.

Pre:

WordType must be an unsigned integral type.

template<typename WordType>
static inline void viskores::cont::Algorithm::Fill(viskores::cont::BitField &bits, WordType word, viskores::Id numBits)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename WordType>
static inline void viskores::cont::Algorithm::Fill(viskores::cont::DeviceAdapterId devId, viskores::cont::BitField &bits, WordType word)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Fills the existing bit field size.

template<typename WordType>
static inline void viskores::cont::Algorithm::Fill(viskores::cont::BitField &bits, WordType word)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Fills the existing bit field size using the runtime device selection mechanism.

template<typename T, typename S>
static inline void viskores::cont::Algorithm::Fill(viskores::cont::DeviceAdapterId devId, viskores::cont::ArrayHandle<T, S> &handle, const T &value)

Fill an array with a value.

Replaces every value in handle with value.

Parameters:
  • devId – Device adapter to run on.

  • handle – Array to fill.

  • value – Value to copy into every entry.

template<typename T, typename S>
static inline void viskores::cont::Algorithm::Fill(viskores::cont::ArrayHandle<T, S> &handle, const T &value)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, typename S>
static inline void viskores::cont::Algorithm::Fill(viskores::cont::DeviceAdapterId devId, viskores::cont::ArrayHandle<T, S> &handle, const T &value, const viskores::Id numValues)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Resizes handle to numValues before filling it.

template<typename T, typename S>
static inline void viskores::cont::Algorithm::Fill(viskores::cont::ArrayHandle<T, S> &handle, const T &value, const viskores::Id numValues)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Resizes handle to numValues and uses the runtime device selection mechanism.

The viskores::cont::Algorithm::Fill() methods fill a viskores::cont::BitField or viskores::cont::ArrayHandle with a specific pattern of bits or values. For a viskores::cont::BitField, it is possible to supply a boolean value or a WordType. For boolean values, all bits are set to 1 if the value is true, or 0 if the value is false. For word masks, the WordType must be an unsigned integral type; this value is stamped across the viskores::cont::BitField. For an viskores::cont::ArrayHandle, the entire array is filled with the provided value. For both types, if a numValues argument is provided the array is resized appropriately and filled with the given value.

Example 4.154 Using the Fill algorithm.
 1    // Fill a BitField
 2    viskores::cont::BitField bits;
 3    bits.Allocate(32);
 4    viskores::cont::Algorithm::Fill(bits, true);
 5    // Will stamp the 8 bit word across 32 bits to result in bits = 0xf0f0f0f0
 6    viskores::cont::Algorithm::Fill(bits, viskores::UInt8(0xf0));
 7    viskores::cont::Algorithm::Fill(bits, viskores::UInt8(0xf0), 16);
 8
 9    // Fill an ArrayHandle
10    viskores::cont::ArrayHandle<viskores::Id> arrayHandle;
11    arrayHandle.Allocate(10);
12    viskores::cont::Algorithm::Fill(arrayHandle, viskores::Id(5));
13    viskores::cont::Algorithm::Fill(arrayHandle, viskores::Id(10), 5);

4.15.7. LowerBounds

template<typename T, class CIn, class CVal, class COut>
static inline void viskores::cont::Algorithm::LowerBounds(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<T, CVal> &values, viskores::cont::ArrayHandle<viskores::Id, COut> &output)

Find lower-bound insertion indices for a vector of query values.

For each value in values, finds the first index in sorted input whose value is greater than or equal to the query value. The results are written to output. This is the vectorized equivalent of std::lower_bound.

Parameters:
  • devId – Device adapter to run on.

  • input – Sorted values to search.

  • values – Query values.

  • output – Output lower-bound indices.

Pre:

input must be sorted according to the default less-than ordering.

template<typename T, class CIn, class CVal, class COut>
static inline void viskores::cont::Algorithm::LowerBounds(const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<T, CVal> &values, viskores::cont::ArrayHandle<viskores::Id, COut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, class CIn, class CVal, class COut, class BinaryCompare>
static inline void viskores::cont::Algorithm::LowerBounds(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<T, CVal> &values, viskores::cont::ArrayHandle<viskores::Id, COut> &output, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_compare as the less-than operation.

input must be sorted according to the same comparison.

template<typename T, class CIn, class CVal, class COut, class BinaryCompare>
static inline void viskores::cont::Algorithm::LowerBounds(const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<T, CVal> &values, viskores::cont::ArrayHandle<viskores::Id, COut> &output, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_compare and the runtime device selection mechanism.

template<class CIn, class COut>
static inline void viskores::cont::Algorithm::LowerBounds(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<viskores::Id, CIn> &input, viskores::cont::ArrayHandle<viskores::Id, COut> &values_output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. In-place specialization for viskores::Id arrays.

The values in values_output are replaced with their lower-bound indices in input. This form is useful for inverting index maps.

template<class CIn, class COut>
static inline void viskores::cont::Algorithm::LowerBounds(const viskores::cont::ArrayHandle<viskores::Id, CIn> &input, viskores::cont::ArrayHandle<viskores::Id, COut> &values_output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. In-place viskores::Id specialization using the runtime device selection mechanism.

The viskores::cont::Algorithm::LowerBounds() method takes three arguments. The first argument is an viskores::cont::ArrayHandle of sorted values. The second argument is another viskores::cont::ArrayHandle of items to find in the first array. viskores::cont::Algorithm::LowerBounds() finds the index of the first item that is greater than or equal to the target value, much like the std::lower_bound STL algorithm. The results are returned in an viskores::cont::ArrayHandle given in the third argument.

There are two specializations of viskores::cont::Algorithm::LowerBounds(). The first takes an additional comparison function that defines the less-than operation. The second specialization takes only two parameters. The first is an viskores::cont::ArrayHandle of sorted viskores::Id values and the second is an viskores::cont::ArrayHandle of viskores::Id values to find in the first list. The results are written back out to the second array. This second specialization is useful for inverting index maps.

Example 4.155 Using the LowerBounds algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> sorted =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>(
 3        { 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 8, 9 });
 4    viskores::cont::ArrayHandle<viskores::Int32> values =
 5      viskores::cont::make_ArrayHandle<viskores::Int32>(
 6        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 7
 8    viskores::cont::ArrayHandle<viskores::Id> output;
 9
10    viskores::cont::Algorithm::LowerBounds(sorted, values, output);
11
12    // output has { 8, 0, 1, 1, 6, 6, 5, 3, 8, 10, 11, 3 }
13
14    viskores::cont::ArrayHandle<viskores::Int32> reverseSorted =
15      viskores::cont::make_ArrayHandle<viskores::Int32>(
16        { 9, 8, 7, 7, 5, 5, 4, 3, 3, 1, 1, 0 });
17
18    viskores::cont::Algorithm::LowerBounds(
19      reverseSorted, values, output, viskores::SortGreater());
20
21    // output has { 2, 11, 9, 9, 4, 4, 6, 7, 2, 1, 0, 7 }

4.15.8. Reduce

template<typename T, typename U, class CIn>
static inline U viskores::cont::Algorithm::Reduce(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, U initialValue)

Reduce an array to a single accumulated value.

Computes the accumulated sum of initialValue and all values in input, using the default addition operation. The accumulation is run in parallel and is not a serial summation, so the operation must be associative to produce consistent results.

Parameters:
  • devId – Device adapter to run on.

  • input – Values to reduce.

  • initialValue – Initial value for the reduction.

Returns:

The reduced total.

template<typename T, typename U, class CIn>
static inline U viskores::cont::Algorithm::Reduce(const viskores::cont::ArrayHandle<T, CIn> &input, U initialValue)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, typename U, class CIn, class BinaryFunctor>
static inline U viskores::cont::Algorithm::Reduce(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, U initialValue, BinaryFunctor binary_functor)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_functor as the reduction operation.

The functor must be associative.

template<typename T, typename U, class CIn, class BinaryFunctor>
static inline U viskores::cont::Algorithm::Reduce(const viskores::cont::ArrayHandle<T, CIn> &input, U initialValue, BinaryFunctor binary_functor)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_functor and the runtime device selection mechanism.

The viskores::cont::Algorithm::Reduce() method takes an input array, initial value, and binary function and computes a “total” of applying the binary function to all entries in the array. The provided binary function must be associative, but it need not be commutative. There is a specialization of viskores::cont::Algorithm::Reduce() that does not take a binary function and computes the sum.

Example 4.156 Using the Reduce algorithm.
1    viskores::cont::ArrayHandle<viskores::Id> input =
2      viskores::cont::make_ArrayHandle<viskores::Id>({ 5, 1, 1, 6 });
3
4    viskores::Id sum = viskores::cont::Algorithm::Reduce(input, viskores::Id{ 0 });
5    // sum is 13
6
7    viskores::Id product =
8      viskores::cont::Algorithm::Reduce(input, viskores::Id{ 1 }, viskores::Multiply());
9    // product is 30

Common Errors

When using the viskores::cont::Algorithm::Reduce() method, it is important to match the types of the initial value, array values, and the arguments of the reduction operator. For example, in Example 4.156, line 4 notice that the initial value, the literal 0, is specifically cast as a viskores::Id (i.e., viskores::Id{ 0 }). This is because the default reduction operator expects the same type for both its input arguments and output argument. Thus the type initial value should match that of the types in the array being reduced. The literal value 0 is likely to be interpreted by the compiler as an int, which may not be the exact same type as viskores::Id. Thus, an explicit type is appropriate here.

4.15.9. ReduceByKey

template<typename T, typename U, class CKeyIn, class CValIn, class CKeyOut, class CValOut, class BinaryFunctor>
static inline void viskores::cont::Algorithm::ReduceByKey(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CKeyIn> &keys, const viskores::cont::ArrayHandle<U, CValIn> &values, viskores::cont::ArrayHandle<T, CKeyOut> &keys_output, viskores::cont::ArrayHandle<U, CValOut> &values_output, BinaryFunctor binary_functor)

Reduce consecutive segments of values that share equal keys.

Partitions keys and values into consecutive segments with identical adjacent keys. Each segment is reduced with binary_functor. The unique segment keys are written to keys_output, and the reduced values are written to values_output.

Parameters:
  • devId – Device adapter to run on.

  • keys – Input keys.

  • values – Input values to reduce.

  • keys_output – Output unique segment keys.

  • values_output – Output reduced segment values.

  • binary_functor – Binary reduction functor.

Pre:

keys and values must have the same number of values.

Pre:

binary_functor must be associative.

template<typename T, typename U, class CKeyIn, class CValIn, class CKeyOut, class CValOut, class BinaryFunctor>
static inline void viskores::cont::Algorithm::ReduceByKey(const viskores::cont::ArrayHandle<T, CKeyIn> &keys, const viskores::cont::ArrayHandle<U, CValIn> &values, viskores::cont::ArrayHandle<T, CKeyOut> &keys_output, viskores::cont::ArrayHandle<U, CValOut> &values_output, BinaryFunctor binary_functor)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

The viskores::cont::Algorithm::ReduceByKey() method works similarly to the viskores::cont::Algorithm::Reduce() method except that it takes an additional array of keys, which must be the same length as the values being reduced. The arrays are partitioned into segments that have identical adjacent keys, and a separate reduction is performed on each partition. The unique keys and reduced values are returned in separate arrays.

Example 4.157 Using the ReduceByKey algorithm.
 1    viskores::cont::ArrayHandle<viskores::Id> keys =
 2      viskores::cont::make_ArrayHandle<viskores::Id>(
 3        { 0, 0, 3, 3, 3, 3, 5, 6, 6, 6, 6, 6 });
 4    viskores::cont::ArrayHandle<viskores::Int32> input =
 5      viskores::cont::make_ArrayHandle<viskores::Int32>(
 6        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 7
 8    viskores::cont::ArrayHandle<viskores::Id> uniqueKeys;
 9    viskores::cont::ArrayHandle<viskores::Int32> sums;
10
11    viskores::cont::Algorithm::ReduceByKey(
12      keys, input, uniqueKeys, sums, viskores::Add());
13
14    // uniqueKeys is { 0, 3, 5, 6 }
15    // sums is { 7, 12, 4, 30 }
16
17    viskores::cont::ArrayHandle<viskores::Int32> products;
18
19    viskores::cont::Algorithm::ReduceByKey(
20      keys, input, uniqueKeys, products, viskores::Multiply());
21
22    // products is { 0, 25, 4, 4536 }

4.15.10. ScanInclusive

template<typename T, class CIn, class COut>
static inline T viskores::cont::Algorithm::ScanInclusive(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output)

Compute an inclusive prefix sum.

Performs a running sum on input and stores the result in output. For inclusive scans, the value at position i includes the input element at position i. For example, the third output value is the sum of the first three input values. If input and output are the same array, the operation is performed in place.

This overload uses the default addition operation. The operation must be associative to produce consistent results.

Parameters:
  • devId – Device adapter to run on.

  • input – Values to scan.

  • output – Inclusive scan output.

Returns:

The sum of all input values.

template<typename T, class CIn, class COut>
static inline T viskores::cont::Algorithm::ScanInclusive(const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, class CIn, class COut, class BinaryFunctor>
static inline T viskores::cont::Algorithm::ScanInclusive(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output, BinaryFunctor binary_functor)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_functor as the scan operation.

The functor must be associative.

template<typename T, class CIn, class COut, class BinaryFunctor>
static inline T viskores::cont::Algorithm::ScanInclusive(const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output, BinaryFunctor binary_functor)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_functor and the runtime device selection mechanism.

The viskores::cont::Algorithm::ScanInclusive() method takes an input and an output viskores::cont::ArrayHandle and performs a running sum on the input array. For inclusive scans, the running sum value for position \(i\) in the input array includes the element at position \(i\). The first value in the output is the same as the first value in the input. The second value in the output is the sum of the first two values in the input. The third value in the output is the sum of the first three values of the input, and so on. If the input and output array are the same, then the operation is done in place. viskores::cont::Algorithm::ScanInclusive() returns the sum of all values in the input. There are two forms of viskores::cont::Algorithm::ScanInclusive(): one performs the sum using addition whereas the other accepts a custom binary function to use as the sum operator.

Example 4.158 Using the ScanInclusive algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> input =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>(
 3        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 4
 5    viskores::cont::ArrayHandle<viskores::Int32> runningSum;
 6
 7    viskores::cont::Algorithm::ScanInclusive(input, runningSum);
 8
 9    // runningSum is { 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50, 53 }
10
11    viskores::cont::ArrayHandle<viskores::Int32> runningMax;
12
13    viskores::cont::Algorithm::ScanInclusive(input, runningMax, viskores::Maximum());
14
15    // runningMax is { 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9, 9 }

4.15.11. ScanInclusiveByKey

template<typename T, typename U, typename KIn, typename VIn, typename VOut, typename BinaryFunctor>
static inline void viskores::cont::Algorithm::ScanInclusiveByKey(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, KIn> &keys, const viskores::cont::ArrayHandle<U, VIn> &values, viskores::cont::ArrayHandle<U, VOut> &values_output, BinaryFunctor binary_functor)

Compute inclusive prefix sums over consecutive equal-key segments.

Works like ScanInclusive, but keys partitions values into consecutive segments with identical adjacent keys. A separate inclusive scan is performed on each segment, and only the scanned values are returned.

Parameters:
  • devId – Device adapter to run on.

  • keys – Input keys defining scan segments.

  • values – Values to scan.

  • values_output – Output scanned values.

  • binary_functor – Binary scan functor.

Pre:

keys and values must have the same number of values.

Pre:

binary_functor must be associative.

template<typename T, typename U, typename KIn, typename VIn, typename VOut, typename BinaryFunctor>
static inline void viskores::cont::Algorithm::ScanInclusiveByKey(const viskores::cont::ArrayHandle<T, KIn> &keys, const viskores::cont::ArrayHandle<U, VIn> &values, viskores::cont::ArrayHandle<U, VOut> &values_output, BinaryFunctor binary_functor)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_functor and the runtime device selection mechanism.

template<typename T, typename U, typename KIn, typename VIn, typename VOut>
static inline void viskores::cont::Algorithm::ScanInclusiveByKey(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, KIn> &keys, const viskores::cont::ArrayHandle<U, VIn> &values, viskores::cont::ArrayHandle<U, VOut> &values_output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the default addition operation.

template<typename T, typename U, typename KIn, typename VIn, typename VOut>
static inline void viskores::cont::Algorithm::ScanInclusiveByKey(const viskores::cont::ArrayHandle<T, KIn> &keys, const viskores::cont::ArrayHandle<U, VIn> &values, viskores::cont::ArrayHandle<U, VOut> &values_output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the default addition operation and the runtime device selection mechanism.

The viskores::cont::Algorithm::ScanInclusiveByKey() method works similarly to the viskores::cont::Algorithm::ScanInclusive() method except that it takes an additional array of keys, which must be the same length as the values being scanned. The arrays are partitioned into segments that have identical adjacent keys, and a separate scan is performed on each partition. Only the scanned values are returned.

Example 4.159 Using the ScanInclusiveByKey algorithm.
 1    viskores::cont::ArrayHandle<viskores::Id> keys =
 2      viskores::cont::make_ArrayHandle<viskores::Id>(
 3        { 0, 0, 3, 3, 3, 3, 5, 6, 6, 6, 6, 6 });
 4    viskores::cont::ArrayHandle<viskores::Int32> input =
 5      viskores::cont::make_ArrayHandle<viskores::Int32>(
 6        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 7
 8    viskores::cont::ArrayHandle<viskores::Int32> runningSums;
 9
10    viskores::cont::Algorithm::ScanInclusiveByKey(keys, input, runningSums);
11
12    // runningSums is { 7, 7, 1, 2, 7, 12, 4, 3, 10, 18, 27, 30 }
13
14    viskores::cont::ArrayHandle<viskores::Int32> runningMaxes;
15
16    viskores::cont::Algorithm::ScanInclusiveByKey(
17      keys, input, runningMaxes, viskores::Maximum());
18
19    // runningMax is { 7, 7, 1, 1, 5, 5, 4, 3, 7, 8, 9, 9 }

4.15.12. ScanExclusive

template<typename T, class CIn, class COut>
static inline T viskores::cont::Algorithm::ScanExclusive(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output)

Compute an exclusive prefix sum.

Performs a running sum on input and stores the result in output. For exclusive scans, the value at position i excludes the input element at position i. With the default addition operation, the first output value is 0, the second output value is the first input value, and so on. If input and output are the same array, the operation is performed in place.

This overload uses the default addition operation. The operation must be associative to produce consistent results.

Parameters:
  • devId – Device adapter to run on.

  • input – Values to scan.

  • output – Exclusive scan output.

Returns:

The sum of all input values.

template<typename T, class CIn, class COut>
static inline T viskores::cont::Algorithm::ScanExclusive(const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, class CIn, class COut, class BinaryFunctor>
static inline T viskores::cont::Algorithm::ScanExclusive(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output, BinaryFunctor binaryFunctor, const T &initialValue)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binaryFunctor as the scan operation and initialValue as the first exclusive value.

The functor must be associative.

template<typename T, class CIn, class COut, class BinaryFunctor>
static inline T viskores::cont::Algorithm::ScanExclusive(const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output, BinaryFunctor binaryFunctor, const T &initialValue)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binaryFunctor, initialValue, and the runtime device selection mechanism.

The viskores::cont::Algorithm::ScanExclusive() method takes an input and an output viskores::cont::ArrayHandle and performs a running sum on the input array. For exclusive scans, the running sum value for position \(i\) in the input array excludes the element at position \(i\). The first value in the output is always 0. The second value in the output is the same as the first value in the input. The third value in the output is the sum of the first two values in the input. The fourth value in the output is the sum of the first three values of the input, and so on. viskores::cont::Algorithm::ScanExclusive() returns the sum of all values in the input. If the input and output array are the same, then the operation is done in place. There are two forms of viskores::cont::Algorithm::ScanExclusive(). The first performs the sum using addition. The second form accepts a custom binary functor to use as the “sum” operator and a custom initial value instead of 0.

Example 4.160 Using the ScanExclusive algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> input =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>(
 3        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 4
 5    viskores::cont::ArrayHandle<viskores::Int32> runningSum;
 6
 7    viskores::cont::Algorithm::ScanExclusive(input, runningSum);
 8
 9    // runningSum is { 0, 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50 }
10
11    viskores::cont::ArrayHandle<viskores::Int32> runningMax;
12
13    viskores::cont::Algorithm::ScanExclusive(input, runningMax, viskores::Maximum(), -1);
14
15    // runningMax is { -1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9 }

4.15.13. ScanExclusiveByKey

template<typename T, typename U, typename KIn, typename VIn, typename VOut, class BinaryFunctor>
static inline void viskores::cont::Algorithm::ScanExclusiveByKey(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, KIn> &keys, const viskores::cont::ArrayHandle<U, VIn> &values, viskores::cont::ArrayHandle<U, VOut> &output, const U &initialValue, BinaryFunctor binaryFunctor)

Compute exclusive prefix sums over consecutive equal-key segments.

Works like ScanExclusive, but keys partitions values into consecutive segments with identical adjacent keys. A separate exclusive scan is performed on each segment, and only the scanned values are returned.

Parameters:
  • devId – Device adapter to run on.

  • keys – Input keys defining scan segments.

  • values – Values to scan.

  • output – Output scanned values.

  • initialValue – Initial value for each segment.

  • binaryFunctor – Binary scan functor.

Pre:

keys and values must have the same number of values.

Pre:

binaryFunctor must be associative.

template<typename T, typename U, typename KIn, typename VIn, typename VOut, class BinaryFunctor>
static inline void viskores::cont::Algorithm::ScanExclusiveByKey(const viskores::cont::ArrayHandle<T, KIn> &keys, const viskores::cont::ArrayHandle<U, VIn> &values, viskores::cont::ArrayHandle<U, VOut> &output, const U &initialValue, BinaryFunctor binaryFunctor)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binaryFunctor, initialValue, and the runtime device selection mechanism.

template<typename T, typename U, class KIn, typename VIn, typename VOut>
static inline void viskores::cont::Algorithm::ScanExclusiveByKey(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, KIn> &keys, const viskores::cont::ArrayHandle<U, VIn> &values, viskores::cont::ArrayHandle<U, VOut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the default addition operation and its zero-initialized value as the initial value for each segment.

template<typename T, typename U, class KIn, typename VIn, typename VOut>
static inline void viskores::cont::Algorithm::ScanExclusiveByKey(const viskores::cont::ArrayHandle<T, KIn> &keys, const viskores::cont::ArrayHandle<U, VIn> &values, viskores::cont::ArrayHandle<U, VOut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the default addition operation and the runtime device selection mechanism.

The viskores::cont::Algorithm::ScanExclusiveByKey() method works similarly to the viskores::cont::Algorithm::ScanExclusive() method except that it takes an additional array of keys, which must be the same length as the values being scanned. The arrays are partitioned into segments that have identical adjacent keys, and a separate scan is performed on each partition. Only the scanned values are returned.

Example 4.161 Using the ScanExclusiveByKey algorithm.
 1    viskores::cont::ArrayHandle<viskores::Id> keys =
 2      viskores::cont::make_ArrayHandle<viskores::Id>(
 3        { 0, 0, 3, 3, 3, 3, 5, 6, 6, 6, 6, 6 });
 4    viskores::cont::ArrayHandle<viskores::Int32> input =
 5      viskores::cont::make_ArrayHandle<viskores::Int32>(
 6        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 7
 8    viskores::cont::ArrayHandle<viskores::Int32> runningSums;
 9
10    viskores::cont::Algorithm::ScanExclusiveByKey(keys, input, runningSums);
11
12    // runningSums is { 0, 7, 0, 1, 2, 7, 0, 0, 3, 10, 18, 27 }
13
14    viskores::cont::ArrayHandle<viskores::Int32> runningMaxes;
15
16    viskores::cont::Algorithm::ScanExclusiveByKey(
17      keys, input, runningMaxes, -1, viskores::Maximum());
18
19    // runningMax is { -1, 7, -1, 1, 1, 5, -1, -1, 3, 7, 8, 9 }

4.15.14. ScanExtended

template<typename T, class CIn, class COut>
static inline void viskores::cont::Algorithm::ScanExtended(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output)

Compute an extended prefix sum.

Computes an extended prefix sum of input and stores it in output. The output array has one more value than the input array. Exclusive scan values are stored in indices [0, size - 1], and inclusive scan values are stored in indices [1, size]. Thus, the first output entry is 0 and the last output entry is the total sum.

Unlike ScanInclusive and ScanExclusive, this method does not return the total. This can be more efficient on some devices because the total does not need to be copied back to the control environment.

This overload uses the default addition operation. The operation must be associative to produce consistent results.

Parameters:
  • devId – Device adapter to run on.

  • input – Values to scan.

  • output – Extended scan output. Resized to input.GetNumberOfValues() + 1.

template<typename T, class CIn, class COut>
static inline void viskores::cont::Algorithm::ScanExtended(const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, class CIn, class COut, class BinaryFunctor>
static inline void viskores::cont::Algorithm::ScanExtended(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output, BinaryFunctor binaryFunctor, const T &initialValue)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binaryFunctor as the scan operation and initialValue as the first output entry.

The functor must be associative.

template<typename T, class CIn, class COut, class BinaryFunctor>
static inline void viskores::cont::Algorithm::ScanExtended(const viskores::cont::ArrayHandle<T, CIn> &input, viskores::cont::ArrayHandle<T, COut> &output, BinaryFunctor binaryFunctor, const T &initialValue)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binaryFunctor, initialValue, and the runtime device selection mechanism.

The viskores::cont::Algorithm::ScanExtended() computes an extended prefix sum operation on the input viskores::cont::ArrayHandle and stores it in a provided output viskores::cont::ArrayHandle. The output array has length 1 greater than the input array. viskores::cont::Algorithm::ScanExtended() is a combination of the viskores::cont::Algorithm::ScanInclusive() and viskores::cont::Algorithm::ScanExclusive() methods. The exclusive scan values are stored in indices \(0\) through \(size-1\). The inclusive scan values are stored in indices \(1\) through \(size\). The first entry in the resulting array is 0, or the specified initial value, like with the exclusive scan. The last entry in the resulting array is the sum total like with the inclusive scan. Unlike the two referenced methods, viskores::cont::Algorithm::ScanExtended() does not return the total sum. By using an viskores::cont::ArrayHandleView, arrays containing both inclusive and exclusive scans can be generated from an extended scan with minimal memory usage by referencing the correct indices in the extended scan output.

This algorithm may be more efficient than viskores::cont::Algorithm::ScanInclusive() and viskores::cont::Algorithm::ScanExclusive() on some devices; this algorithm may be able to avoid copying the total sum to the control environment to return. viskores::cont::Algorithm::ScanExtended() is similar to the STL partial sum function, with the exception that it does not perform a serial summation. This means that if you have defined a custom plus operator for T it must be associative, or you will get inconsistent results.

The first form performs the sum using addition. The second form accepts a custom binary functor to use as the operator and a custom initial value instead of 0.

Example 4.162 Using the ScanExtended algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> input =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>(
 3        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 4
 5    viskores::cont::ArrayHandle<viskores::Int32> runningSum;
 6    viskores::cont::Algorithm::ScanExtended(input, runningSum);
 7
 8    // runningSum is { 0, 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50, 53 }
 9
10    viskores::cont::ArrayHandle<viskores::Int32> runningMax;
11    viskores::cont::Algorithm::ScanExtended(input, runningMax, viskores::Maximum(), -1);
12
13    // runningMax is { -1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9, 9 }

viskores::cont::Algorithm::ScanExtended() can be used to create a running sum that is quickly reversible. If you subtract two consecutive values of a scan, you get back the original value. This is convenient if you need both the input and output of a scan; you can throw away the input and use differences of the output. However, viskores::cont::Algorithm::ScanInclusive() does not write out the initial value, so you cannot get back the original value at the beginning without a special condition. Likewise, viskores::cont::Algorithm::ScanExclusive() does not write out the total sum, so you cannot get back the original value at the end without a special condition. viskores::cont::Algorithm::ScanExtended() solves this problem by extending the array by 1. The original value for index \(i\) can be retrieved by subtracting the scan value at index \(i\) from the value at index \(i+1\) anywhere in the array, including at the beginning and end. This is particularly useful for storing packed arrays in structures like viskores::cont::CellSetExplicit and viskores::cont::ArrayHandleGroupVecVariable.

4.15.15. Schedule

The viskores::cont::Algorithm::Schedule() method takes a functor as its first argument and invokes it a number of times specified by the second argument. It should be assumed that each invocation of the functor occurs on a separate thread, although in practice there could be some thread sharing.

There are two versions of the viskores::cont::Algorithm::Schedule() method. The first version takes a viskores::Id and invokes the functor that number of times. The second version takes a viskores::Id3 and invokes the functor once for every entry in a 3D array of the given dimensions.

template<typename Functor>
static inline void viskores::cont::Algorithm::Schedule(viskores::cont::DeviceAdapterId devId, Functor functor, viskores::Id numInstances)

Schedule a 1D set of functor invocations on a device.

Invokes functor once for each index in [0, numInstances). Each invocation should be assumed to occur on a separate thread, although a device implementation may share threads in practice.

The functor must provide a const operator()(viskores::Id) and must subclass viskores::exec::FunctorBase for execution-environment error handling.

Parameters:
  • devId – Device adapter to run on.

  • functor – Function object to invoke.

  • numInstances – Number of invocations.

template<typename ...Hints, typename Functor>
static inline void viskores::cont::Algorithm::Schedule(viskores::cont::internal::HintList<Hints...> hints, Functor functor, viskores::Id numInstances)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses hints to influence runtime device selection.

template<typename Functor>
static inline void viskores::cont::Algorithm::Schedule(Functor functor, viskores::Id numInstances)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename Functor>
static inline void viskores::cont::Algorithm::Schedule(viskores::cont::DeviceAdapterId devId, Functor functor, viskores::Id3 rangeMax)

Schedule a 3D set of functor invocations on a device.

Invokes functor once for every entry in a 3D index space with dimensions rangeMax. Each invocation should be assumed to occur on a separate thread, although a device implementation may share threads in practice.

The functor must provide a const operator()(viskores::Id3) and must subclass viskores::exec::FunctorBase for execution-environment error handling.

Parameters:
  • devId – Device adapter to run on.

  • functor – Function object to invoke.

  • rangeMax – Dimensions of the 3D index space.

template<typename ...Hints, typename Functor>
static inline void viskores::cont::Algorithm::Schedule(viskores::cont::internal::HintList<Hints...> hints, Functor functor, viskores::Id3 rangeMax)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses hints to influence runtime device selection.

template<typename Functor>
static inline void viskores::cont::Algorithm::Schedule(Functor functor, viskores::Id3 rangeMax)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

The functor is expected to be an object with a const overloaded parentheses operator. The operator takes as a parameter the index of the invocation, which is either a viskores::Id or a viskores::Id3 depending on what version of viskores::cont::Algorithm::Schedule() is being used. The functor must also subclass viskores::exec::FunctorBase, which provides the error handling facilities for the execution environment. viskores::exec::FunctorBase contains a public method named viskores::exec::FunctorBase::RaiseError() that takes a message and will cause a viskores::cont::ErrorExecution exception to be thrown in the control environment.

4.15.16. Sort

template<typename T, class Storage>
static inline void viskores::cont::Algorithm::Sort(viskores::cont::DeviceAdapterId devId, viskores::cont::ArrayHandle<T, Storage> &values)

Sort an array in ascending order.

Performs an unstable in-place sort of values using the default less-than ordering.

Parameters:
  • devId – Device adapter to run on.

  • values – Array to sort in place.

template<typename T, class Storage>
static inline void viskores::cont::Algorithm::Sort(viskores::cont::ArrayHandle<T, Storage> &values)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, class Storage, class BinaryCompare>
static inline void viskores::cont::Algorithm::Sort(viskores::cont::DeviceAdapterId devId, viskores::cont::ArrayHandle<T, Storage> &values, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Sorts according to binary_compare, which must define a strict weak ordering.

template<typename T, class Storage, class BinaryCompare>
static inline void viskores::cont::Algorithm::Sort(viskores::cont::ArrayHandle<T, Storage> &values, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_compare and the runtime device selection mechanism.

The viskores::cont::Algorithm::Sort() method provides an unstable sort of an array. There are two forms of the viskores::cont::Algorithm::Sort() method. The first takes an viskores::cont::ArrayHandle and sorts the values in place. The second takes an additional argument that is a functor that provides the comparison operation for the sort.

Example 4.163 Using the Sort algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> array =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>(
 3        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 4
 5    viskores::cont::Algorithm::Sort(array);
 6
 7    // array has { 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 8, 9 }
 8
 9    viskores::cont::Algorithm::Sort(array, viskores::SortGreater());
10
11    // array has { 9, 8, 7, 7, 5, 5, 4, 3, 3, 1, 1, 0 }

4.15.17. SortByKey

template<typename T, typename U, class StorageT, class StorageU>
static inline void viskores::cont::Algorithm::SortByKey(viskores::cont::DeviceAdapterId devId, viskores::cont::ArrayHandle<T, StorageT> &keys, viskores::cont::ArrayHandle<U, StorageU> &values)

Sort keys and reorder associated values.

Performs an unstable in-place sort of keys using the default less-than ordering. values is reordered so each value remains paired with its original key.

Parameters:
  • devId – Device adapter to run on.

  • keys – Keys to sort in place.

  • values – Values to reorder with their corresponding keys.

Pre:

keys and values must have the same number of values.

template<typename T, typename U, class StorageT, class StorageU>
static inline void viskores::cont::Algorithm::SortByKey(viskores::cont::ArrayHandle<T, StorageT> &keys, viskores::cont::ArrayHandle<U, StorageU> &values)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, typename U, class StorageT, class StorageU, class BinaryCompare>
static inline void viskores::cont::Algorithm::SortByKey(viskores::cont::DeviceAdapterId devId, viskores::cont::ArrayHandle<T, StorageT> &keys, viskores::cont::ArrayHandle<U, StorageU> &values, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Sorts keys according to binary_compare, which must define a strict weak ordering.

template<typename T, typename U, class StorageT, class StorageU, class BinaryCompare>
static inline void viskores::cont::Algorithm::SortByKey(viskores::cont::ArrayHandle<T, StorageT> &keys, viskores::cont::ArrayHandle<U, StorageU> &values, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_compare and the runtime device selection mechanism.

The viskores::cont::Algorithm::SortByKey() method works similarly to the viskores::cont::Algorithm::Sort() method except that it takes two viskores::cont::ArrayHandle objects: an array of keys and a corresponding array of values. The sort orders the array of keys in ascending values and also reorders the values so they remain paired with the same key. Like viskores::cont::Algorithm::Sort(), viskores::cont::Algorithm::SortByKey() has a version that sorts by the default less-than operator and a version that accepts a custom comparison functor.

Example 4.164 Using the SortByKey algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> keys =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>({ 7, 0, 1, 5, 4, 8, 9, 3 });
 3    viskores::cont::ArrayHandle<viskores::Id> values =
 4      viskores::cont::make_ArrayHandle<viskores::Id>({ 0, 1, 2, 3, 4, 5, 6, 7 });
 5
 6    viskores::cont::Algorithm::SortByKey(keys, values);
 7
 8    // keys has   { 0, 1, 3, 4, 5, 7, 8, 9 }
 9    // values has { 1, 2, 7, 4, 3, 0, 5, 6 }
10
11    viskores::cont::Algorithm::SortByKey(keys, values, viskores::SortGreater());
12
13    // keys has   { 9, 8, 7, 5, 4, 3, 1, 0 }
14    // values has { 6, 5, 0, 3, 4, 7, 2, 1 }

4.15.18. Synchronize

The viskores::cont::Algorithm::Synchronize() method waits for any asynchronous operations running on the device to complete and then returns.

static inline void viskores::cont::Algorithm::Synchronize(viskores::cont::DeviceAdapterId devId)

Wait for asynchronous device operations to complete.

Parameters:

devId – Device adapter to synchronize.

static inline void viskores::cont::Algorithm::Synchronize()

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Synchronizes any selected runtime device.

4.15.19. Transform

template<typename T, typename U, typename V, typename StorageT, typename StorageU, typename StorageV, typename BinaryFunctor>
static inline void viskores::cont::Algorithm::Transform(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, StorageT> &input1, const viskores::cont::ArrayHandle<U, StorageU> &input2, viskores::cont::ArrayHandle<V, StorageV> &output, BinaryFunctor binaryFunctor)

Apply a binary operation element-wise to two arrays.

Applies binaryFunctor to corresponding values from input1 and input2 and stores the result in output. The input arrays do not need to have the same number of values. The output array is resized to the smaller input length.

Parameters:
  • devId – Device adapter to run on.

  • input1 – First input array.

  • input2 – Second input array.

  • output – Destination array.

  • binaryFunctor – Binary operation applied to corresponding input values.

template<typename T, typename U, typename V, typename StorageT, typename StorageU, typename StorageV, typename BinaryFunctor>
static inline void viskores::cont::Algorithm::Transform(const viskores::cont::ArrayHandle<T, StorageT> &input1, const viskores::cont::ArrayHandle<U, StorageU> &input2, viskores::cont::ArrayHandle<V, StorageV> &output, BinaryFunctor binaryFunctor)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

The viskores::cont::Algorithm::Transform() method applies a given binary operation function element-wise on two input arrays, storing the result in a provided output array. The number of elements in the input arrays do not have to be the same; the output array will have the same number of elements as the smaller of the two input arrays.

Example 4.165 Using the Transform algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> input1 =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>(
 3        { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
 4    viskores::cont::ArrayHandle<viskores::Int32> input2 =
 5      viskores::cont::make_ArrayHandle<viskores::Int32>({ 2, 3, 4, 5, 6, 7, 8, 9, 10 });
 6
 7    viskores::cont::ArrayHandle<viskores::Int32> output;
 8    viskores::cont::Algorithm::Transform(input1, input2, output, viskores::Sum());
 9
10    // output is { 3, 5, 7, 9, 11, 13, 15, 17, 19 }

4.15.20. Unique

The viskores::cont::Algorithm::Unique() method removes all duplicate values in an viskores::cont::ArrayHandle. The method will only find duplicates if they are adjacent to each other in the array. The easiest way to ensure that duplicate values are adjacent is to sort the array first.

There are two versions of viskores::cont::Algorithm::Unique(). The first uses the equals operator to compare entries. The second accepts a binary functor to perform the comparisons.

template<typename T, class Storage>
static inline void viskores::cont::Algorithm::Unique(viskores::cont::DeviceAdapterId devId, viskores::cont::ArrayHandle<T, Storage> &values)

Remove adjacent duplicate values from an array.

Removes duplicate values from values in place. Only adjacent duplicate values are removed. Sort the array first when all duplicate values should be adjacent and removed.

This overload uses viskores::Equal to compare values.

Parameters:
  • devId – Device adapter to run on.

  • values – Array to compact to unique adjacent values.

template<typename T, class Storage>
static inline void viskores::cont::Algorithm::Unique(viskores::cont::ArrayHandle<T, Storage> &values)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, class Storage, class BinaryCompare>
static inline void viskores::cont::Algorithm::Unique(viskores::cont::DeviceAdapterId devId, viskores::cont::ArrayHandle<T, Storage> &values, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_compare to decide when two adjacent values are equal.

The predicate must return true when the two values should be considered the same.

template<typename T, class Storage, class BinaryCompare>
static inline void viskores::cont::Algorithm::Unique(viskores::cont::ArrayHandle<T, Storage> &values, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_compare and the runtime device selection mechanism.

Example 4.166 Using the Unique algorithm.
 1  struct AlmostEqualFunctor
 2  {
 3    VISKORES_EXEC_CONT bool operator()(viskores::Float64 x, viskores::Float64 y) const
 4    {
 5      return (viskores::Abs(x - y) < 0.1);
 6    }
 7  };
 8
 9    viskores::cont::ArrayHandle<viskores::Int32> values =
10      viskores::cont::make_ArrayHandle<viskores::Int32>(
11        { 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 7, 9 });
12
13    viskores::cont::Algorithm::Unique(values);
14
15    // values has {0, 1, 3, 4, 5, 7, 9}
16
17    viskores::cont::ArrayHandle<viskores::Float64> fvalues =
18      viskores::cont::make_ArrayHandle<viskores::Float64>(
19        { 0.0, 0.001, 0.0, 1.5, 1.499, 2.0 });
20
21    viskores::cont::Algorithm::Unique(fvalues, AlmostEqualFunctor());
22
23    // values has {0.0, 1.5, 2.0}

4.15.21. UpperBounds

template<typename T, class CIn, class CVal, class COut>
static inline void viskores::cont::Algorithm::UpperBounds(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<T, CVal> &values, viskores::cont::ArrayHandle<viskores::Id, COut> &output)

Find upper-bound insertion indices for a vector of query values.

For each value in values, finds the first index in sorted input whose value is greater than the query value. The results are written to output. This is the vectorized equivalent of std::upper_bound.

Parameters:
  • devId – Device adapter to run on.

  • input – Sorted values to search.

  • values – Query values.

  • output – Output upper-bound indices.

Pre:

input must be sorted according to the default less-than ordering.

template<typename T, class CIn, class CVal, class COut>
static inline void viskores::cont::Algorithm::UpperBounds(const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<T, CVal> &values, viskores::cont::ArrayHandle<viskores::Id, COut> &output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses the runtime device selection mechanism.

template<typename T, class CIn, class CVal, class COut, class BinaryCompare>
static inline void viskores::cont::Algorithm::UpperBounds(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<T, CVal> &values, viskores::cont::ArrayHandle<viskores::Id, COut> &output, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_compare as the less-than operation.

input must be sorted according to the same comparison.

template<typename T, class CIn, class CVal, class COut, class BinaryCompare>
static inline void viskores::cont::Algorithm::UpperBounds(const viskores::cont::ArrayHandle<T, CIn> &input, const viskores::cont::ArrayHandle<T, CVal> &values, viskores::cont::ArrayHandle<viskores::Id, COut> &output, BinaryCompare binary_compare)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. Uses binary_compare and the runtime device selection mechanism.

template<class CIn, class COut>
static inline void viskores::cont::Algorithm::UpperBounds(viskores::cont::DeviceAdapterId devId, const viskores::cont::ArrayHandle<viskores::Id, CIn> &input, viskores::cont::ArrayHandle<viskores::Id, COut> &values_output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. In-place specialization for viskores::Id arrays.

The values in values_output are replaced with their upper-bound indices in input. This form is useful for inverting index maps.

template<class CIn, class COut>
static inline void viskores::cont::Algorithm::UpperBounds(const viskores::cont::ArrayHandle<viskores::Id, CIn> &input, viskores::cont::ArrayHandle<viskores::Id, COut> &values_output)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. In-place viskores::Id specialization using the runtime device selection mechanism.

The viskores::cont::Algorithm::UpperBounds() method takes three arguments. The first argument is an viskores::cont::ArrayHandle of sorted values. The second argument is another viskores::cont::ArrayHandle of items to find in the first array. viskores::cont::Algorithm::UpperBounds() finds the index of the first item that is greater than the target value, much like the std::upper_bound STL algorithm. The results are returned in an viskores::cont::ArrayHandle given in the third argument.

There are two specializations of viskores::cont::Algorithm::UpperBounds(). The first takes an additional comparison function that defines the less-than operation. The second takes only two parameters. The first is an viskores::cont::ArrayHandle of sorted viskores::Id values and the second is an viskores::cont::ArrayHandle of viskores::Id values to find in the first list. The results are written back out to the second array. This second specialization is useful for inverting index maps.

Example 4.167 Using the UpperBounds algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> sorted =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>(
 3        { 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 8, 9 });
 4    viskores::cont::ArrayHandle<viskores::Int32> values =
 5      viskores::cont::make_ArrayHandle<viskores::Int32>(
 6        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 7
 8    viskores::cont::ArrayHandle<viskores::Id> output;
 9
10    viskores::cont::Algorithm::UpperBounds(sorted, values, output);
11
12    // output has { 10, 1, 3, 3, 8, 8, 6, 5, 10, 11, 12, 5 }
13
14    viskores::cont::ArrayHandle<viskores::Int32> reverseSorted =
15      viskores::cont::make_ArrayHandle<viskores::Int32>(
16        { 9, 8, 7, 7, 5, 5, 4, 3, 3, 1, 1, 0 });
17
18    viskores::cont::Algorithm::UpperBounds(
19      reverseSorted, values, output, viskores::SortGreater());
20
21    // output has { 4, 12, 11, 11, 6, 6, 7, 9, 4, 2, 1, 9 }

4.15.22. Specifying the Device Adapter

When you call a method in viskores::cont::Algorithm, a device is automatically specified based on available hardware and the Viskores runtime state. However, if you want to use a specific device, you can specify that device by passing either a viskores::cont::DeviceAdapterId or a device adapter tag as the first argument to any of these methods.

Example 4.168 Using a DeviceAdapter with viskores::cont::Algorithm.
 1    viskores::cont::ArrayHandle<viskores::Int32> input =
 2      viskores::cont::make_ArrayHandle<viskores::Int32>(
 3        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
 4
 5    viskores::cont::ArrayHandle<viskores::Int32> output_no_device_specified;
 6
 7    viskores::cont::ArrayHandle<viskores::Int32> output_device_specified;
 8
 9    viskores::cont::Algorithm::Copy(input, output_no_device_specified);
10
11    //optional we can pass the device or int id number
12    viskores::cont::Algorithm::Copy(
13      viskores::cont::DeviceAdapterTagSerial(), input, output_device_specified);
14
15    // output has { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 }

4.15.23. Predicates and Operators

Viskores follows certain design philosophies consistent with the functional programming paradigm. This assists in making implementations device agnostic and ensuring that various functions operate correctly and efficiently in multiple environments. Many basic operations, such as binary and unary comparisons and predicates, are implemented as templated functors. These are mostly re-implementations of basic C++ STL functors that can be used in the Viskores execution environment.

Strictly using a functor by itself adds little in the way of functionality to the code. Their use is demonstrated more when used as parameters to one of the viskores::cont::Algorithm methods discussed earlier in this chapter. Currently, Viskores provides 3 categories of functors: Unary Predicates, Binary Predicates, and Binary Operators.

4.15.23.1. Unary Predicates

Unary Predicates are functors that take a single parameter and return a boolean value. These types of functors are useful in determining if values have been initialized or zeroed out correctly.

viskores::IsZeroInitialized

Returns true if the argument is the identity of its type.

viskores::NotZeroInitialized

Returns true if the argument is not the identity of its type.

viskores::LogicalNot

Returns true if and only if the argument is false. Requires that the argument type is convertible to a boolean or implements the ! operator.

struct IsZeroInitialized

Predicate that takes a single argument x, and returns True if it is the identity of the Type T.

struct NotZeroInitialized

Predicate that takes a single argument x, and returns True if it isn’t the identity of the Type T.

struct LogicalNot

Predicate that takes a single argument x, and returns True if and only if x is false.

Note: Requires Type T to be convertible to bool or implement the ! operator.

Example 4.169 Basic unary predicates.
1    viskores::IsZeroInitialized isZero;
2    viskores::NotZeroInitialized isNotZero;
3    viskores::LogicalNot logicalNot;
4
5    bool zeroIsZero = isZero(0);
6    bool fiveIsNotZero = isNotZero(5);
7    bool notFalse = logicalNot(false);

4.15.23.2. Binary Predicates

Binary Predicates take two parameters and return a single boolean value. These types of functors are used when comparing two different parameters for some sort of condition.

viskores::Equal

Returns true if and only if the first argument is equal to the second argument. Requires that the argument type implements the == operator.

viskores::NotEqual

Returns true if and only if the first argument is not equal to the second argument. Requires that the argument type implements the != operator.

viskores::SortLess

Returns true if and only if the first argument is less than the second argument. Requires that the argument type implements the < operator.

viskores::SortGreater

Returns true if and only if the first argument is greater than the second argument. Requires that the argument type implements the < operator; the comparison is inverted internally.

viskores::LogicalAnd

Returns true if and only if the first argument and the second argument are true. Requires that the argument type is convertible to a boolean or implements the && operator.

viskores::LogicalOr

Returns true if and only if the first argument or the second argument is true. Requires that the argument type is convertible to a boolean or implements the || operator.

struct Equal

Binary Predicate that takes two arguments argument x, and y and returns True if and only if x is equal to y.

Note

: Requires that types T and U are comparable with !=.

struct NotEqual

Binary Predicate that takes two arguments argument x, and y and returns True if and only if x is not equal to y.

Note

: Requires that types T and U are comparable with !=.

struct SortLess

Binary Predicate that takes two arguments argument x, and y and returns True if and only if x is less than y.

Note

: Requires that types T and U are comparable with <.

struct SortGreater

Binary Predicate that takes two arguments argument x, and y and returns True if and only if x is greater than y.

Note

: Requires that types T and U are comparable via operator<(U, T).

struct LogicalAnd

Binary Predicate that takes two arguments argument x, and y and returns True if and only if x and y are True.

Note

: Requires that types T and U are comparable with &&.

struct LogicalOr

Binary Predicate that takes two arguments argument x, and y and returns True if and only if x or y is True.

Note

: Requires that types T and U are comparable with ||.

Example 4.170 Basic binary predicates.
 1    viskores::Equal equal;
 2    viskores::NotEqual notEqual;
 3    viskores::SortLess less;
 4    viskores::SortGreater greater;
 5    viskores::LogicalAnd logicalAnd;
 6    viskores::LogicalOr logicalOr;
 7
 8    bool same = equal(4, 4);
 9    bool different = notEqual(4, 5);
10    bool ordered = less(4, 5);
11    bool reverseOrdered = greater(5, 4);
12    bool both = logicalAnd(true, true);
13    bool either = logicalOr(false, true);
14    // All above are true.

4.15.23.3. Binary Operators

Binary Operators take two parameters and return a single value, usually of the same type as the input arguments. These types of functors are useful when performing reductions or transformations of a data set.

viskores::Sum

Returns the sum of two arguments. Requires that the argument type implements the + operator.

viskores::Product

Returns the product, multiplication, of two arguments. Requires that the argument type implements the * operator.

viskores::Maximum

Returns the larger of two arguments. Requires that the argument type implements the < operator.

viskores::Minimum

Returns the smaller of two arguments. Requires that the argument type implements the < operator.

viskores::MinAndMax

Returns a viskores::Vec<T, 2> that represents the minimum and maximum values. Requires that the argument type implements the viskores::Min() and viskores::Max() functions.

viskores::BitwiseAnd

Returns the bitwise and of two arguments. Requires that the argument type implements the & operator.

viskores::BitwiseOr

Returns the bitwise or of two arguments. Requires that the argument type implements the | operator.

viskores::BitwiseXor

Returns the bitwise xor of two arguments. Requires that the argument type implements the ^ operator.

struct Sum

Binary Predicate that takes two arguments argument x, and y and returns sum (addition) of the two values.

Note

Requires a suitable definition of operator+(T, U).

struct Product

Binary Predicate that takes two arguments argument x, and y and returns product (multiplication) of the two values.

Note

Requires a suitable definition of operator*(T, U).

struct Maximum

Binary Predicate that takes two arguments argument x, and y and returns the x if x > y otherwise returns y.

Note

Requires a suitable definition of bool operator<(T, U) and that T and U share a common type.

struct Minimum

Binary Predicate that takes two arguments argument x, and y and returns the x if x < y otherwise returns y.

Note

Requires a suitable definition of bool operator<(T, U) and that T and U share a common type.

template<typename T>
struct MinAndMax

Binary Predicate that takes two arguments argument x, and y and returns a viskores::Vec<T,2> that represents the minimum and maximum values.

Note: Requires Type T implement the viskores::Min and viskores::Max functions.

struct BitwiseAnd

Binary Predicate that takes two arguments argument x, and y and returns the bitwise operation x&y

Note

Requires a suitable definition of operator&(T, U).

struct BitwiseOr

Binary Predicate that takes two arguments argument x, and y and returns the bitwise operation x|y

Note

Requires a suitable definition of operator&(T, U).

struct BitwiseXor

Binary Predicate that takes two arguments argument x, and y and returns the bitwise operation x^y

Note

Requires a suitable definition of operator&(T, U).

Example 4.171 Basic binary operators.
 1    viskores::Sum sum;
 2    viskores::Product product;
 3    viskores::Maximum maximum;
 4    viskores::Minimum minimum;
 5    viskores::MinAndMax<viskores::Int32> minAndMax;
 6    viskores::BitwiseAnd bitwiseAnd;
 7    viskores::BitwiseOr bitwiseOr;
 8    viskores::BitwiseXor bitwiseXor;
 9
10    viskores::Int32 added = sum(2, 3);                         // added == 5
11    viskores::Int32 multiplied = product(2, 3);                // multiplied == 6
12    viskores::Int32 larger = maximum(2, 3);                    // larger == 3
13    viskores::Int32 smaller = minimum(2, 3);                   // smaller == 2
14    viskores::Vec<viskores::Int32, 2> range = minAndMax(2, 3); // range == [2, 3]
15    viskores::Int32 anded = bitwiseAnd(0x0f, 0x33);            // anded == 0x03
16    viskores::Int32 ored = bitwiseOr(0x0f, 0x33);              // ored == 0x3f
17    viskores::Int32 xored = bitwiseXor(0x0f, 0x33);            // xored == 0x3c

4.15.23.4. Creating Custom Comparators

In addition to using the built-in operators and predicates, it is possible to create your own custom functors to be used in one of the viskores::cont::Algorithm methods. Custom operator and predicate functors can be used to apply specific logic used to manipulate your data. The following example creates a unary predicate that checks if the input is a power of 2.

Example 4.172 Custom unary predicate implementation.
1  struct IsPowerOfTwo
2  {
3    VISKORES_EXEC bool operator()(viskores::Id value) const
4    {
5      return (value > 0) && ((value & (value - 1)) == 0);
6    }
7  };
Example 4.173 Custom unary predicate usage.
1    viskores::cont::ArrayHandle<viskores::Id> input =
2      viskores::cont::make_ArrayHandle<viskores::Id>({ 0, 1, 2, 3, 4, 5, 8, 12, 16 });
3    viskores::cont::ArrayHandle<viskores::Id> output;
4
5    viskores::cont::Algorithm::CopyIf(input, input, output, IsPowerOfTwo());
6
7    // output has { 1, 2, 4, 8, 16 }