Files
lammps/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsAdjacentDifference.cpp
2024-12-13 09:23:03 -07:00

266 lines
8.2 KiB
C++

//@HEADER
// ************************************************************************
//
// Kokkos v. 4.0
// Copyright (2022) National Technology & Engineering
// Solutions of Sandia, LLC (NTESS).
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
// See https://kokkos.org/LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//@HEADER
#include <TestStdAlgorithmsCommon.hpp>
#include <std_algorithms/Kokkos_BeginEnd.hpp>
#include <std_algorithms/Kokkos_AdjacentDifference.hpp>
#include <utility>
#include <numeric>
namespace Test {
namespace stdalgos {
namespace AdjacentDifference {
namespace KE = Kokkos::Experimental;
template <class DestViewType>
void fill_view(DestViewType dest_view, const std::string& name) {
// we need to be careful because dest_view might not be deep copyable
// for instance strided layout
using value_type = typename DestViewType::value_type;
const std::size_t ext = dest_view.extent(0);
auto aux_view =
create_deep_copyable_compatible_view_with_same_extent(dest_view);
auto aux_v_h = create_mirror_view(Kokkos::HostSpace(), aux_view);
if (name == "empty") {
// no op
}
else if (name == "one-element") {
aux_v_h(0) = static_cast<value_type>(1);
}
else if (name == "two-elements-a") {
aux_v_h(0) = static_cast<value_type>(1);
aux_v_h(1) = static_cast<value_type>(2);
}
else if (name == "two-elements-b") {
aux_v_h(0) = static_cast<value_type>(2);
aux_v_h(1) = static_cast<value_type>(-1);
}
else if (name == "small-a") {
for (std::size_t i = 0; i < ext; ++i) {
aux_v_h(i) = static_cast<value_type>(i) * 2;
}
}
else if (name == "small-b") {
for (std::size_t i = 0; i < ext; ++i) {
aux_v_h(i) = static_cast<value_type>(i) * 3;
}
aux_v_h(5) = static_cast<value_type>(-15);
}
else if (name == "medium-a") {
for (std::size_t i = 0; i < ext; ++i) {
aux_v_h(i) = static_cast<value_type>(i) * 2;
}
}
else if (name == "medium-b") {
for (std::size_t i = 0; i < ext; ++i) {
aux_v_h(i) = static_cast<value_type>(i) * 2;
}
aux_v_h(4) = static_cast<value_type>(-1);
}
else if (name == "large-a") {
for (std::size_t i = 0; i < ext; ++i) {
aux_v_h(i) = static_cast<value_type>(-100) + static_cast<value_type>(i);
}
}
else if (name == "large-b") {
for (std::size_t i = 0; i < ext; ++i) {
aux_v_h(i) = static_cast<value_type>(-100) + static_cast<value_type>(i);
}
aux_v_h(156) = static_cast<value_type>(-250);
}
else {
FAIL() << "invalid choice";
}
Kokkos::deep_copy(aux_view, aux_v_h);
CopyFunctor<decltype(aux_view), DestViewType> F1(aux_view, dest_view);
Kokkos::parallel_for("copy", dest_view.extent(0), F1);
}
template <class TestViewType, class... Args>
auto compute_gold(TestViewType test_view, const std::string& name,
Args... args /* copy on purpose */) {
// we need to be careful because test_view might not be deep copyable
// for instance strided layout
const std::size_t ext = test_view.extent(0);
// create a deep copyable clone of test_view
auto test_view_dc = create_deep_copyable_compatible_clone(test_view);
auto test_view_dc_h =
create_mirror_view_and_copy(Kokkos::HostSpace(), test_view_dc);
// create gold deep copyable view
auto gold_view =
create_deep_copyable_compatible_view_with_same_extent(test_view);
auto gold_view_h = create_mirror_view(Kokkos::HostSpace(), gold_view);
// compute gold solution on host and deep copy to device
if (name == "empty") {
return gold_view;
} else {
using value_type = typename TestViewType::value_type;
std::vector<value_type> tmp(ext);
for (std::size_t i = 0; i < ext; ++i) {
tmp[i] = test_view_dc_h(i);
}
// run adj-diff on tmp directly
std::adjacent_difference(tmp.begin(), tmp.end(), tmp.begin(),
std::forward<Args>(args)...);
// copy from tmp to gold_h
for (std::size_t i = 0; i < ext; ++i) {
gold_view_h(i) = tmp[i];
}
// deep_copy to device
Kokkos::deep_copy(gold_view, gold_view_h);
return gold_view;
}
}
template <class TestViewType, class GoldViewType>
void verify_data(TestViewType test_view, GoldViewType gold) {
// we need to be careful because test_view might not be deep copyable
// for instance strided layout
auto test_view_dc = create_deep_copyable_compatible_clone(test_view);
auto test_view_dc_h =
create_mirror_view_and_copy(Kokkos::HostSpace(), test_view_dc);
// gold is deep_copyable for sure
const auto gold_h = create_mirror_view_and_copy(Kokkos::HostSpace(), gold);
for (std::size_t i = 0; i < test_view.extent(0); ++i) {
ASSERT_EQ(gold_h(i), test_view_dc_h(i));
}
}
template <class ValueType1, class ValueType2 = ValueType1,
class RetType = ValueType2>
struct CustomBinaryOpFunctor {
KOKKOS_INLINE_FUNCTION
RetType operator()(const ValueType1& a, const ValueType2& b) const {
return a * b;
}
};
template <class ValueType1, class ValueType2 = ValueType1,
class RetType = ValueType2>
struct DefaultBinaryOpFunctor {
KOKKOS_INLINE_FUNCTION
RetType operator()(const ValueType1& a, const ValueType2& b) const {
return a - b;
}
};
template <class Tag, class ValueType, class InfoType, class... Args>
void run_single_scenario(const InfoType& scenario_info,
Args... args /* copy on purpose */) {
const auto name = std::get<0>(scenario_info);
const std::size_t view_ext = std::get<1>(scenario_info);
auto view_from =
create_view<ValueType>(Tag{}, view_ext, "adj_diff_from_view");
fill_view(view_from, name);
const auto gold = compute_gold(view_from, name, args...);
{
auto view_dest =
create_view<ValueType>(Tag{}, view_ext, "adj_diff_dest_view");
auto res1 = KE::adjacent_difference(exespace(), KE::cbegin(view_from),
KE::cend(view_from),
KE::begin(view_dest), args...);
ASSERT_EQ(res1, KE::end(view_dest));
verify_data(view_dest, gold);
}
{
auto view_dest =
create_view<ValueType>(Tag{}, view_ext, "adj_diff_dest_view");
auto res2 = KE::adjacent_difference(
"label", exespace(), KE::cbegin(view_from), KE::cend(view_from),
KE::begin(view_dest), args...);
ASSERT_EQ(res2, KE::end(view_dest));
verify_data(view_dest, gold);
}
{
auto view_dest =
create_view<ValueType>(Tag{}, view_ext, "adj_diff_dest_view");
auto res3 =
KE::adjacent_difference(exespace(), view_from, view_dest, args...);
ASSERT_EQ(res3, KE::end(view_dest));
verify_data(view_dest, gold);
}
{
auto view_dest =
create_view<ValueType>(Tag{}, view_ext, "adj_diff_dest_view");
auto res4 = KE::adjacent_difference("label", exespace(), view_from,
view_dest, args...);
ASSERT_EQ(res4, KE::end(view_dest));
verify_data(view_dest, gold);
}
Kokkos::fence();
}
template <class Tag, class ValueType, class... Args>
void run_all_scenarios(Args... args /* copy on purpose */) {
// if (0 < sizeof...(args)) {
// std::cout << "adjacent_difference: " << view_tag_to_string(Tag{})
// << ", custom binary op, all overloads \n";
// } else {
// std::cout << "adjacent_difference: " << view_tag_to_string(Tag{})
// << ", default binary op, all overloads \n";
// }
for (const auto& it : default_scenarios) {
run_single_scenario<Tag, ValueType>(it, args...);
}
}
TEST(std_algorithms_numerics_ops_test, adjecent_difference) {
using value_type = double;
run_all_scenarios<DynamicTag, value_type>();
run_all_scenarios<StridedTwoTag, value_type>();
run_all_scenarios<StridedThreeTag, value_type>();
using custom_binary_op = CustomBinaryOpFunctor<value_type>;
run_all_scenarios<DynamicTag, value_type>(custom_binary_op{});
run_all_scenarios<StridedTwoTag, value_type>(custom_binary_op{});
run_all_scenarios<StridedThreeTag, value_type>(custom_binary_op{});
}
} // namespace AdjacentDifference
} // namespace stdalgos
} // namespace Test