//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. // // Copyright 2016 Sandia Corporation. // Copyright 2016 UT-Battelle, LLC. // Copyright 2016 Los Alamos National Security. // // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #ifndef vtk_m_worklet_Keys_h #define vtk_m_worklet_Keys_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace vtkm { namespace worklet { /// \brief Manage keys for a \c WorkletReduceByKey. /// /// The \c WorkletReduceByKey worklet (and its associated \c /// DispatcherReduceByKey) take an array of keys for its input domain, find all /// identical keys, and runs a worklet that produces a single value for every /// key given all matching values. This class is used as the associated input /// for the keys input domain. /// /// \c Keys is templated on the key array handle type and accepts an instance /// of this array handle as its constructor. It builds the internal structures /// needed to use the keys. /// /// The same \c Keys structure can be used for multiple different \c Invoke of /// different dispatchers. When used in this way, the processing done in the \c /// Keys structure is reused for all the \c Invoke. This is more efficient than /// creating a different \c Keys structure for each \c Invoke. /// template class Keys { public: using KeyType = _KeyType; using KeyArrayHandleType = vtkm::cont::ArrayHandle; VTKM_CONT Keys() {} /// \b Construct a Keys class from an array of keys. /// /// Given an array of keys, construct a \c Keys class that will manage /// using these keys to perform reduce-by-key operations. /// /// WARNING: This constructor will sort the keys array! If you need the /// keys in the original order after calling this constructor, then make /// a deep copy of the keys first. /// template VTKM_CONT Keys(vtkm::cont::ArrayHandle keys, Device) { this->BuildArrays(keys, Device()); } VTKM_CONT vtkm::Id GetInputRange() const { return this->UniqueKeys.GetNumberOfValues(); } VTKM_CONT KeyArrayHandleType GetUniqueKeys() const { return this->UniqueKeys; } VTKM_CONT vtkm::cont::ArrayHandle GetSortedValuesMap() const { return this->SortedValuesMap; } VTKM_CONT vtkm::cont::ArrayHandle GetOffsets() const { return this->Offsets; } VTKM_CONT vtkm::cont::ArrayHandle GetCounts() const { return this->Counts; } VTKM_CONT vtkm::Id GetNumberOfValues() const { return this->SortedValuesMap.GetNumberOfValues(); } template struct ExecutionTypes { using KeyPortal = typename KeyArrayHandleType::template ExecutionTypes::PortalConst; using IdPortal = typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst; using IdComponentPortal = typename vtkm::cont::ArrayHandle< vtkm::IdComponent>::template ExecutionTypes::PortalConst; using Lookup = vtkm::exec::internal::ReduceByKeyLookup; }; template VTKM_CONT typename ExecutionTypes::Lookup PrepareForInput(Device) const { return typename ExecutionTypes::Lookup( this->UniqueKeys.PrepareForInput(Device()), this->SortedValuesMap.PrepareForInput(Device()), this->Offsets.PrepareForInput(Device()), this->Counts.PrepareForInput(Device())); } VTKM_CONT bool operator==(const vtkm::worklet::Keys& other) const { return ((this->UniqueKeys == other.UniqueKeys) && (this->SortedValuesMap == other.SortedValuesMap) && (this->Offsets == other.Offsets) && (this->Counts == other.Counts)); } VTKM_CONT bool operator!=(const vtkm::worklet::Keys& other) const { return !(*this == other); } private: KeyArrayHandleType UniqueKeys; vtkm::cont::ArrayHandle SortedValuesMap; vtkm::cont::ArrayHandle Offsets; vtkm::cont::ArrayHandle Counts; template VTKM_CONT void BuildArrays(KeyArrayType& keys, Device) { using Algorithm = vtkm::cont::DeviceAdapterAlgorithm; vtkm::Id numKeys = keys.GetNumberOfValues(); Algorithm::Copy(vtkm::cont::ArrayHandleIndex(numKeys), this->SortedValuesMap); // TODO: Do we need the ability to specify a comparison functor for sort? Algorithm::SortByKey(keys, this->SortedValuesMap); // Find the unique keys and the number of values per key. Algorithm::ReduceByKey(keys, vtkm::cont::ArrayHandleConstant(1, numKeys), this->UniqueKeys, this->Counts, vtkm::Sum()); // Get the offsets from the counts with a scan. vtkm::Id offsetsTotal = Algorithm::ScanExclusive( vtkm::cont::make_ArrayHandleCast(this->Counts, vtkm::Id()), this->Offsets); VTKM_ASSERT(offsetsTotal == numKeys); // Sanity check (void)offsetsTotal; // Shut up, compiler } }; } } // namespace vtkm::worklet // Here we implement the type checks and transports that rely on the Keys // class. We implement them here because the Keys class is not accessible to // the arg classes. (The worklet package depends on the cont and exec packages, // not the other way around.) namespace vtkm { namespace cont { namespace arg { template struct TypeCheck> { static const bool value = true; }; template struct Transport, Device> { using ContObjectType = vtkm::worklet::Keys; using ExecObjectType = typename ContObjectType::template ExecutionTypes::Lookup; VTKM_CONT ExecObjectType operator()(const ContObjectType& object, const ContObjectType& inputDomain, vtkm::Id, vtkm::Id) const { if (object != inputDomain) { throw vtkm::cont::ErrorBadValue("A Keys object must be the input domain."); } return object.PrepareForInput(Device()); } // If you get a compile error here, it means that you have used a KeysIn // tag in your ControlSignature that was not marked as the InputDomain. template VTKM_CONT ExecObjectType operator()(const ContObjectType&, const InputDomainType&, vtkm::Id, vtkm::Id) const = delete; }; template struct Transport { VTKM_IS_ARRAY_HANDLE(ArrayHandleType); using ContObjectType = ArrayHandleType; using IdArrayType = vtkm::cont::ArrayHandle; using PermutedArrayType = vtkm::cont::ArrayHandlePermutation; using GroupedArrayType = vtkm::cont::ArrayHandleGroupVecVariable; using ExecObjectType = typename GroupedArrayType::template ExecutionTypes::PortalConst; template VTKM_CONT ExecObjectType operator()(const ContObjectType& object, const vtkm::worklet::Keys& keys, vtkm::Id, vtkm::Id) const { if (object.GetNumberOfValues() != keys.GetNumberOfValues()) { throw vtkm::cont::ErrorBadValue("Input values array is wrong size."); } PermutedArrayType permutedArray(keys.GetSortedValuesMap(), object); GroupedArrayType groupedArray(permutedArray, keys.GetOffsets()); // There is a bit of an issue here where groupedArray goes out of scope, // and array portals usually rely on the associated array handle // maintaining the resources it points to. However, the entire state of the // portal should be self contained except for the data managed by the // object argument, which should stay in scope. return groupedArray.PrepareForInput(Device()); } }; template struct Transport { VTKM_IS_ARRAY_HANDLE(ArrayHandleType); using ContObjectType = ArrayHandleType; using IdArrayType = vtkm::cont::ArrayHandle; using PermutedArrayType = vtkm::cont::ArrayHandlePermutation; using GroupedArrayType = vtkm::cont::ArrayHandleGroupVecVariable; using ExecObjectType = typename GroupedArrayType::template ExecutionTypes::Portal; template VTKM_CONT ExecObjectType operator()(ContObjectType object, const vtkm::worklet::Keys& keys, vtkm::Id, vtkm::Id) const { if (object.GetNumberOfValues() != keys.GetNumberOfValues()) { throw vtkm::cont::ErrorBadValue("Input/output values array is wrong size."); } PermutedArrayType permutedArray(keys.GetSortedValuesMap(), object); GroupedArrayType groupedArray(permutedArray, keys.GetOffsets()); // There is a bit of an issue here where groupedArray goes out of scope, // and array portals usually rely on the associated array handle // maintaining the resources it points to. However, the entire state of the // portal should be self contained except for the data managed by the // object argument, which should stay in scope. return groupedArray.PrepareForInPlace(Device()); } }; template struct Transport { VTKM_IS_ARRAY_HANDLE(ArrayHandleType); using ContObjectType = ArrayHandleType; using IdArrayType = vtkm::cont::ArrayHandle; using PermutedArrayType = vtkm::cont::ArrayHandlePermutation; using GroupedArrayType = vtkm::cont::ArrayHandleGroupVecVariable; using ExecObjectType = typename GroupedArrayType::template ExecutionTypes::Portal; template VTKM_CONT ExecObjectType operator()(ContObjectType object, const vtkm::worklet::Keys& keys, vtkm::Id, vtkm::Id) const { // The PrepareForOutput for ArrayHandleGroupVecVariable and // ArrayHandlePermutation cannot determine the actual size expected for the // target array (object), so we have to make sure it gets allocated here. object.PrepareForOutput(keys.GetNumberOfValues(), Device()); PermutedArrayType permutedArray(keys.GetSortedValuesMap(), object); GroupedArrayType groupedArray(permutedArray, keys.GetOffsets()); // There is a bit of an issue here where groupedArray goes out of scope, // and array portals usually rely on the associated array handle // maintaining the resources it points to. However, the entire state of the // portal should be self contained except for the data managed by the // object argument, which should stay in scope. return groupedArray.PrepareForOutput(keys.GetInputRange(), Device()); } }; } } } // namespace vtkm::cont::arg #endif //vtk_m_worklet_Keys_h