Added ListReduce, ListAll, and ListAny

These new features to VTK-m lists allow you to compute a single value
from a list. `ListReduce` allows you to compute a value based on a
predicate. `ListAll` and `ListAny` use this feature to determine if all
or any of a list of `true_type` or `false_type` objects are true.
This commit is contained in:
Kenneth Moreland 2022-02-09 11:27:30 -07:00
parent feac74a22a
commit 124f08381b
5 changed files with 238 additions and 1 deletions

@ -0,0 +1,57 @@
# New `vtkm::List` features
New features were added to those available in `vtkm/List.h`. These new
features provide new operations on lists.
## Reductions
The new `vtkm::ListReduce` allows a reduction on a list. This template
takes three arguments: a `vtkm::List`, an operation, and an initial value.
The operation is itself a template that has two type arguments.
`vtkm::ListReduce` applies the initial value and the first item of the list
to the operator. The result of that template is then iteratively applied to
the operator with the next item in the list and so on.
``` cpp
// Operation to use
template <typename T1, typename T2>
using Add = std::integral_constant<typename T1::type, T1::value + T2::value>;
using MyList = vtkm::List<std::integral_constant<int, 25>,
std::integral_constant<int, 60>,
std::integral_constant<int, 87>,
std::integral_constant<int, 62>>;
using MySum = vtkm::ListReduce<MyList, Add, std::integral_constant<int, 0>>;
// MySum becomes std::integral_constant<int, 234> (25+60+87+62 = 234)
```
## All and Any
Because they are very common, two reductions that are automatically
supported are `vtkm::ListAll` and `vtkm::ListAny`. These both take a
`vtkm::List` containing either `std::true_type` or `std::false_type` (or
some other "compatible" type that has a constant static `bool` named
`value`). `vtkm::ListAll` will become `std::false_type` if any of the
entries in the list are `std::false_type`. `vtkm::ListAny` becomes
`std::true_type` if any of the entires in the list are `std::true_type`.
``` cpp
using MyList = vtkm::List<std::integral_constant<int, 25>,
std::integral_constant<int, 60>,
std::integral_constant<int, 87>,
std::integral_constant<int, 62>>;
template <typename T>
using IsEven = std::integral_constant<bool, ((T % 2) == 0)>;
// Note that vtkm::ListTransform<MyList, IsEven> becomes
// vtkm::List<std::false_type, std::true_type, std::false_type, std::true_type>
using AllEven = vtkm::ListAll<vtkm::ListTransform<MyList, IsEven>>;
// AllEven becomes std::false_type
using AnyEven = vtkm::ListAny<vtkm::ListTransform<MyList, IsEven>>;
// AnyEven becomes std::true_type
```

@ -12,6 +12,8 @@
#include <vtkm/Types.h>
#include <vtkm/internal/Meta.h>
namespace vtkm
{
@ -807,6 +809,81 @@ template <typename List1, typename List2>
using ListCross =
typename detail::ListCrossImpl<internal::AsList<List1>, internal::AsList<List2>>::type;
namespace detail
{
template <typename L, template <typename T1, typename T2> class Operator, typename Result>
struct ListReduceImpl;
template <template <typename T1, typename T2> class Operator, typename Result>
struct ListReduceImpl<vtkm::List<>, Operator, Result>
{
using type = Result;
};
template <typename T0,
typename... Ts,
template <typename O1, typename O2>
class Operator,
typename Result>
struct ListReduceImpl<vtkm::List<T0, Ts...>, Operator, Result>
{
using type = typename ListReduceImpl<vtkm::List<Ts...>, Operator, Operator<Result, T0>>::type;
};
template <typename T0,
typename T1,
typename T2,
typename T3,
typename T4,
typename T5,
typename T6,
typename T7,
typename T8,
typename... Ts,
template <typename O1, typename O2>
class Operator,
typename Result>
struct ListReduceImpl<vtkm::List<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>, Operator, Result>
{
using type = typename ListReduceImpl<
vtkm::List<T8, Ts...>,
Operator,
typename ListReduceImpl<vtkm::List<T0, T1, T2, T3, T4, T5, T6, T7>, Operator, Result>::type>::
type;
};
} // namespace detail
/// \brief Reduces a list to a single type using an operator.
///
/// `ListReduce` takes a `vtkm::List`, an operator template, and an initial type. `ListReduce`
/// first applies the initial type and the first item in the list to the operator and gets
/// that type. That then applies the operator to that result and the next item in the list.
/// This continues until a single value is left.
///
template <typename List, template <typename T1, typename T2> class Operator, typename Initial>
using ListReduce = typename detail::ListReduceImpl<internal::AsList<List>, Operator, Initial>::type;
/// \brief Determines whether all the types in the list are "true."
///
/// `ListAll` expects a `vtkm::List` with types that have a `value` that is either true or false
/// (such as `std::true_type` and `std::false_type`. Resolves to `std::true_type` if all the types
/// are true, `std::false_type` otherwise. If the list is empty, resolves to `std::true_type`.
///
template <typename List>
using ListAll = vtkm::ListReduce<List, vtkm::internal::meta::And, std::true_type>;
/// \brief Determines whether any of the types in the list are "true."
///
/// `ListAll` expects a `vtkm::List` with types that have a `value` that is either true or false
/// (such as `std::true_type` and `std::false_type`. Resolves to `std::true_type` if any of the
/// types are true, `std::false_type` otherwise. If the list is empty, resolves to
/// `std::false_type`.
///
template <typename List>
using ListAny = vtkm::ListReduce<List, vtkm::internal::meta::Or, std::false_type>;
#undef VTKM_CHECK_LIST_SIZE
} // namespace vtkm

@ -68,6 +68,7 @@ set(headers
IndexTag.h
IndicesExtrude.h
Invocation.h
Meta.h
Unreachable.h
VariantImpl.h
VariantImplDetail.h

73
vtkm/internal/Meta.h Normal file

@ -0,0 +1,73 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_internal_Meta_h
#define vtk_m_internal_Meta_h
// This header file contains templates that are helpful with template metaprogramming.
// Perhaps one day these structures can be exposed in the public interface, but the
// interface is a little wonky.
#include <type_traits>
namespace vtkm
{
namespace internal
{
namespace meta
{
/// A simple `struct` that holds a type without having to actually make the type object.
template <typename T>
struct Type
{
using type = T;
};
namespace detail
{
template <typename T1, typename T2>
struct AndImpl : std::integral_constant<bool, T1::value && T2::value>
{
};
template <typename T1, typename T2>
struct OrImpl : std::integral_constant<bool, T1::value || T2::value>
{
};
template <typename T>
struct NotImpl : std::integral_constant<bool, !T::value>
{
};
} // namespace detail
/// Expects two types, both with a `value` constant static value (like a `std::integral_constant`).
/// Resolves to a `std::integral_constant<bool, B>` where B is `T1::value && T2::value`.
template <typename T1, typename T2>
using And = typename detail::AndImpl<T1, T2>::type;
/// Expects two types, both with a `value` constant static value (like a `std::integral_constant`).
/// Resolves to a `std::integral_constant<bool, B>` where B is `T1::value || T2::value`.
template <typename T1, typename T2>
using Or = typename detail::OrImpl<T1, T2>::type;
/// Expects a type with a `value` constant static value (like a std::integral_constant`).
/// Resolves to a `std::integral_constant<bool, B>` where B is `!T::value`.
template <typename T>
using Not = typename detail::NotImpl<T>::type;
}
}
} // namespace vtkm::internal::meta
#endif //vtk_m_internal_Meta_h

@ -18,7 +18,7 @@ namespace
{
template <int N>
struct TestClass
struct TestClass : std::integral_constant<int, N>
{
};
@ -63,6 +63,14 @@ struct EvenPredicate<TestClass<N>> : std::integral_constant<bool, (N % 2) == 0>
{
};
template <typename T>
using OddPredicate = vtkm::internal::meta::Not<EvenPredicate<T>>;
template <typename T1, typename T2>
struct AddOperator : TestClass<T1::value + T2::value>
{
};
template <typename T1, typename T2>
void CheckSame(T1, T2)
{
@ -275,6 +283,27 @@ void TestLists()
VTKM_TEST_ASSERT(!vtkm::ListHas<RepeatList, TestClass<0>>::value);
VTKM_TEST_ASSERT(vtkm::ListHas<RepeatList, TestClass<1>>::value);
VTKM_TEST_ASSERT(vtkm::ListHas<RepeatList, TestClass<14>>::value);
std::cout << "ListReduce" << std::endl;
using Zero = std::integral_constant<int, 0>;
VTKM_TEST_ASSERT((vtkm::ListReduce<SimpleCount, AddOperator, Zero>::value == 10));
VTKM_TEST_ASSERT((vtkm::ListReduce<EvenList, AddOperator, Zero>::value == 20));
VTKM_TEST_ASSERT((vtkm::ListReduce<LongList, AddOperator, Zero>::value == 105));
VTKM_TEST_ASSERT((vtkm::ListReduce<RepeatList, AddOperator, Zero>::value == 27));
std::cout << "ListAll" << std::endl;
VTKM_TEST_ASSERT(
(vtkm::ListAll<vtkm::ListTransform<SimpleCount, EvenPredicate>>::value == false));
VTKM_TEST_ASSERT((vtkm::ListAll<vtkm::ListTransform<EvenList, EvenPredicate>>::value == true));
VTKM_TEST_ASSERT((vtkm::ListAll<vtkm::ListTransform<LongList, EvenPredicate>>::value == false));
VTKM_TEST_ASSERT((vtkm::ListAll<vtkm::List<>>::value == true));
std::cout << "ListAny" << std::endl;
VTKM_TEST_ASSERT((vtkm::ListAny<vtkm::ListTransform<SimpleCount, EvenPredicate>>::value == true));
VTKM_TEST_ASSERT((vtkm::ListAny<vtkm::ListTransform<EvenList, EvenPredicate>>::value == true));
VTKM_TEST_ASSERT((vtkm::ListAny<vtkm::ListTransform<EvenList, OddPredicate>>::value == false));
VTKM_TEST_ASSERT((vtkm::ListAny<vtkm::ListTransform<LongList, EvenPredicate>>::value == true));
VTKM_TEST_ASSERT((vtkm::ListAny<vtkm::List<>>::value == false));
}
} // anonymous namespace