diff --git a/.gitignore b/.gitignore index cb84f20f..15752655 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ build* .vscode* .idea* .cache/ +CMakeCache.txt +CMakeFiles/ diff --git a/include/ygm/container/bag.hpp b/include/ygm/container/bag.hpp index 206d8160..dc7e5f5b 100644 --- a/include/ygm/container/bag.hpp +++ b/include/ygm/container/bag.hpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include namespace ygm::container { @@ -394,7 +394,7 @@ class bag : public detail::base_async_insert_value, std::tuple>, * @brief Shuffle elements held locally with a default random number generator */ void local_shuffle() { - ygm::default_random_engine<> r(m_comm, std::random_device()()); + ygm::random::default_random_engine<> r(m_comm, std::random_device()()); local_shuffle(r); } @@ -425,7 +425,7 @@ class bag : public detail::base_async_insert_value, std::tuple>, * generator */ void global_shuffle() { - ygm::default_random_engine<> r(m_comm, std::random_device()()); + ygm::random::default_random_engine<> r(m_comm, std::random_device()()); global_shuffle(r); } diff --git a/include/ygm/random.hpp b/include/ygm/random.hpp deleted file mode 100644 index c30374c7..00000000 --- a/include/ygm/random.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2019-2025 Lawrence Livermore National Security, LLC and other YGM -// Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -namespace ygm { - -/// @brief A simple offset rng alias -/// @tparam RandomEngine The underlying random engine, e.g. std::mt19937 -template -using default_random_engine = - ygm::detail::random_engine; - -} // namespace ygm \ No newline at end of file diff --git a/include/ygm/random/alias_table.hpp b/include/ygm/random/alias_table.hpp new file mode 100644 index 00000000..581ffd8e --- /dev/null +++ b/include/ygm/random/alias_table.hpp @@ -0,0 +1,280 @@ + +// Copyright 2019-2025 Lawrence Livermore National Security, LLC and other YGM +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +namespace ygm::random { + +template +concept pair_like_and_convertible_to_weighted_item = + (requires { typename T::first_type; typename T::second_type; } && + std::is_convertible_v>) || + (std::tuple_size_v == 2 && + std::is_convertible_v, Item> && + std::is_convertible_v, double>); + +template +class alias_table { + + public: + using self_type = alias_table; + + struct item { + Item id; + double weight; + + template + void serialize(Archive& ar) { + ar(id, weight); + } + }; + + struct table_item { + // p / m_avg_weight = prob item a is selected. 1 - p / m_avg_weight is prob b is selected. + double p; + Item a; + Item b; + }; + + template + requires ygm::container::detail::HasForAll && + ygm::container::detail::SingleItemTuple && + pair_like_and_convertible_to_weighted_item> + alias_table(ygm::comm &comm, YGMContainer &c, + std::optional seed = std::nullopt) + : m_comm(comm), pthis(this), m_rank_dist(0, comm.size()-1), + m_bucket_weight_dist(0.0, 1.0), m_rng(comm) { + if (seed) { + m_rng = default_random_engine<>(comm, *seed); + } + c.for_all([&](const auto& id_weight_pair){ + m_local_items.emplace_back(std::get<0>(id_weight_pair), std::get<1>(id_weight_pair)); + }); + build_alias_table(); + } + + template + requires ygm::container::detail::HasForAll && + ygm::container::detail::DoubleItemTuple && + pair_like_and_convertible_to_weighted_item + alias_table(ygm::comm &comm, YGMContainer &c, + std::optional seed = std::nullopt) + : m_comm(comm), pthis(this), m_rank_dist(0, comm.size()-1), + m_bucket_weight_dist(0.0, 1.0), m_rng(comm) { + if (seed) { + m_rng = default_random_engine<>(comm, *seed); + } + c.for_all([&](const auto &id, const auto &weight){ + m_local_items.emplace_back(id,weight); + }); + build_alias_table(); + } + + template + requires ygm::container::detail::STLContainer && + pair_like_and_convertible_to_weighted_item< + Item, typename STLContainer::value_type> + alias_table(ygm::comm &comm, STLContainer &c, + std::optional seed = std::nullopt) + : m_comm(comm), pthis(this), m_rank_dist(0, comm.size()-1), + m_bucket_weight_dist(0.0, 1.0), m_rng(comm) { + if (seed) { + m_rng = default_random_engine<>(comm, *seed); + } + for (const auto& [id, weight] : c) { + m_local_items.emplace_back(id, weight); + } + build_alias_table(); + } + + private: + + void build_alias_table() { + m_comm.barrier(); + balance_weight(); + m_comm.barrier(); + build_local_alias_table(); + m_local_items.clear(); + } + + void balance_weight() { + double local_weight = 0.0; + for (uint32_t i = 0; i < m_local_items.size(); i++) { + local_weight += m_local_items[i].weight; + } + double global_weight = ygm::sum(local_weight, m_comm); + double prfx_sum_weight = ygm::prefix_sum(local_weight, m_comm); + + // target_weight = Amount of weight each rank should have after balancing + double target_weight = global_weight / m_comm.size(); + int dest_rank = prfx_sum_weight / target_weight; + // Spillage weight i.e. weight being contributed by other processors to dest's rank local distribution + double curr_weight = std::fmod(prfx_sum_weight, target_weight); + + std::vector new_local_items; + using ygm_items_ptr = ygm::ygm_ptr>; + ygm_items_ptr ptr_new_items = m_comm.make_ygm_ptr(new_local_items); + m_comm.barrier(); + + std::vector items_to_send; + // WARNING: size of m_local_items can grow during loop. Do not use iterators or pointers in the loop. + for (uint64_t i = 0; i < m_local_items.size(); i++) { + item local_item = m_local_items[i]; + if (curr_weight + local_item.weight >= target_weight) { + double remaining_weight = curr_weight + local_item.weight - target_weight; + double weight_to_send = local_item.weight - remaining_weight; + curr_weight += weight_to_send; + item item_to_send = {local_item.id, weight_to_send}; + items_to_send.push_back(item_to_send); + + if (dest_rank < m_comm.size()) { + // Moves weights to dest_rank's new_local_items + m_comm.async(dest_rank, [](std::vector items, ygm_items_ptr new_items_ptr) { + new_items_ptr->insert(new_items_ptr->end(), items.begin(), items.end()); + }, items_to_send, ptr_new_items); + } + + // Handle case where item weight is large enough to span multiple rank's alias tables + if (remaining_weight >= target_weight) { + m_local_items.push_back({local_item.id, remaining_weight}); + curr_weight = 0; + } else { + curr_weight = remaining_weight; + } + items_to_send.clear(); + if (curr_weight != 0) { + items_to_send.push_back({local_item.id, curr_weight}); + } + dest_rank++; + } else { + items_to_send.push_back(local_item); + curr_weight += local_item.weight; + } + } + + // Need to handle items left in items to send. Must also account for floating point errors. + if (items_to_send.size() > 0 && dest_rank < m_comm.size()) { + m_comm.async(dest_rank, [](std::vector items, ygm_items_ptr new_items_ptr) { + new_items_ptr->insert(new_items_ptr->end(), items.begin(), items.end()); + }, items_to_send, ptr_new_items); + } + + m_comm.barrier(); + std::swap(new_local_items, m_local_items); + + YGM_ASSERT_RELEASE(m_local_items.size() > 0); + YGM_ASSERT_RELEASE(is_balanced(target_weight)); + } + + bool is_balanced(double target) { + double local_weight = 0.0; + for (const auto& itm : m_local_items) { + local_weight += itm.weight; + } + double dif = std::abs(target - local_weight); + YGM_ASSERT_RELEASE(dif < 1e-6); + + m_comm.barrier(); + auto equal = [this](double a, double b){ + return (std::abs(a - b) < 1e-6); + }; + bool balanced = ygm::is_same(local_weight, m_comm, equal); + return balanced; + } + + void build_local_alias_table() { + double local_weight = 0.0; + for (const auto& itm : m_local_items) { + local_weight += itm.weight; + } + double avg_weight = local_weight / m_local_items.size(); + + // Implementation of Vose's algorithm, utilized Keith Schwarz numerically stable version + // https://www.keithschwarz.com/darts-dice-coins/ + std::vector heavy_items; + std::vector light_items; + for (auto& itm : m_local_items) { + if (itm.weight < avg_weight) { + light_items.push_back(itm); + } else { + heavy_items.push_back(itm); + } + } + + while (!light_items.empty() && !heavy_items.empty()) { + item& l = light_items.back(); + item& h = heavy_items.back(); + table_item tbl_itm = {l.weight, l.id, h.id}; + m_local_alias_table.push_back(tbl_itm); + h.weight = (h.weight + l.weight) - avg_weight; + light_items.pop_back(); + if (h.weight < avg_weight) { + light_items.push_back(h); + heavy_items.pop_back(); + } + } + + // Either heavy items or light_items is empty, need to flush the non empty + // vector and add them to the alias table with a p value of avg_weight + while (!heavy_items.empty()) { + item& h = heavy_items.back(); + table_item tbl_itm = {avg_weight, h.id, Item()}; + m_local_alias_table.push_back(tbl_itm); + heavy_items.pop_back(); + } + while (!light_items.empty()) { + item& l = light_items.back(); + table_item tbl_itm = {avg_weight, l.id, Item()}; + m_local_alias_table.push_back(tbl_itm); + light_items.pop_back(); + } + m_comm.barrier(); + m_num_items_uniform_dist = std::uniform_int_distribution(0,m_local_alias_table.size()-1); + m_bucket_weight_dist = std::uniform_real_distribution(0,avg_weight); + m_avg_weight = avg_weight; + } + + public: + + template + void async_sample(Visitor&& visitor, const VisitorArgs &...args) { + + auto sample_wrapper = [visitor](auto ptr_a_tbl, const VisitorArgs &...args) { + table_item tbl_itm = ptr_a_tbl->m_local_alias_table[ptr_a_tbl->m_num_items_uniform_dist(ptr_a_tbl->m_rng)]; + Item s = tbl_itm.a; + if (tbl_itm.p < ptr_a_tbl->m_avg_weight) { + double f = ptr_a_tbl->m_bucket_weight_dist(ptr_a_tbl->m_rng); + if (f > tbl_itm.p) { + s = tbl_itm.b; + } + } + ygm::meta::apply_optional(visitor, std::make_tuple(ptr_a_tbl), std::forward_as_tuple(s, args...)); + }; + + uint32_t dest_rank = m_rank_dist(m_rng); + m_comm.async(dest_rank, sample_wrapper, pthis, std::forward(args)...); + } + + private: + ygm::comm& m_comm; + ygm::ygm_ptr pthis; + std::vector m_local_items; + std::vector m_local_alias_table; + std::uniform_int_distribution m_rank_dist; + std::uniform_int_distribution m_num_items_uniform_dist; + std::uniform_real_distribution m_zero_one_dist; + std::uniform_real_distribution m_bucket_weight_dist; + double m_avg_weight; + ygm::random::default_random_engine<> m_rng; +}; + +} // namespace ygm::random \ No newline at end of file diff --git a/include/ygm/random/random.hpp b/include/ygm/random/random.hpp new file mode 100644 index 00000000..6936c5fd --- /dev/null +++ b/include/ygm/random/random.hpp @@ -0,0 +1,57 @@ +// Copyright 2019-2025 Lawrence Livermore National Security, LLC and other YGM +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +namespace ygm::random { + + +/// @brief Applies a simple offset to the specified seed according to rank index +/// @tparam ResultType The random number (seed) type; defaults to std::mt19937 +/// @param comm The ygm::comm to be used +/// @param seed The specified seed +/// @return simply returns seed + rank +template +ResultType simple_offset(ygm::comm &comm, ResultType seed) { + return seed + comm.rank(); +} + +/// @brief A wrapper around a per-rank random engine that manipulates each +/// rank's seed according to a specified strategy +/// @tparam RandomEngine The underlying random engine, e.g. std::mt19337 +/// @tparam Function A `(ygm::comm, result_type) -> result_type` function that +/// modifies seeds for each rank +template +class random_engine { + public: + using rng_type = RandomEngine; + using result_type = typename RandomEngine::result_type; + + random_engine(ygm::comm &comm, result_type seed = std::random_device{}()) + : m_rng(Function(comm, seed)), m_seed(Function(comm, seed)) {} + + result_type operator()() { return m_rng(); } + + constexpr const result_type &seed() const { return m_seed; } + + static constexpr result_type min() { return rng_type::min(); } + static constexpr result_type max() { return rng_type::max(); } + + private: + rng_type m_rng; + result_type m_seed; +}; + +/// @brief A simple offset rng alias +/// @tparam RandomEngine The underlying random engine, e.g. std::mt19937 +template +using default_random_engine = + random_engine; + +} // namespace ygm::random \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9ff58df3..2348bda1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -71,6 +71,7 @@ add_ygm_test(test_recursion_progress) add_ygm_test(test_gather_topk) add_ygm_test(test_reduce) add_ygm_test(test_transform) +add_ygm_test(test_alias_table) add_ygm_test(test_local_visit) add_ygm_test(test_progress_indicator) add_ygm_test(test_boost_string) diff --git a/test/test_alias_table.cpp b/test/test_alias_table.cpp new file mode 100644 index 00000000..6bc821b1 --- /dev/null +++ b/test/test_alias_table.cpp @@ -0,0 +1,243 @@ +// Copyright 2019-2021 Lawrence Livermore National Security, LLC and other YGM +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: MIT + +#undef NDEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + + ygm::comm world(&argc, &argv); + int seed = 42; + ygm::random::default_random_engine<> ygm_rng(world, seed); + + // + // Testing various constructors + { + uint32_t n_items_per_rank = 1000; + const int max_item_weight = 100; + std::uniform_real_distribution dist(0, max_item_weight); + { // Constructing from ygm::container::bag + ygm::container::bag> bag_of_items(world); + for (uint32_t i = 0; i < n_items_per_rank; i++) { + uint32_t id = world.rank() + i * world.size(); + double w = dist(ygm_rng); + bag_of_items.async_insert({id, w}); + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, bag_of_items, ygm_rng()); + } + { // Constructing from ygm::container::map + ygm::container::map map_of_items(world); + for (uint32_t i = 0; i < n_items_per_rank; i++) { + uint32_t id = world.rank() + i * world.size(); + double w = dist(ygm_rng); + map_of_items.async_insert({id, w}); + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, map_of_items, ygm_rng()); + } + { // Constructing from ygm::container::array + ygm::container::array array_of_weights(world, n_items_per_rank*world.size()); + for (uint32_t i = 0; i < n_items_per_rank; i++) { + uint32_t id = world.rank() + i * world.size(); + double w = dist(ygm_rng); + array_of_weights.async_set(id,w); + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, array_of_weights, ygm_rng()); + } + { // Constructing from std::vector + std::vector> vec_of_items; + for (uint32_t i = 0; i < n_items_per_rank; i++) { + uint32_t id = world.rank() + i * world.size(); + double w = dist(ygm_rng); + vec_of_items.push_back({id,w}); + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, vec_of_items, ygm_rng()); + } + { // Constructing from std::map + std::map items_map; + for (uint32_t i = 0; i < n_items_per_rank; i++) { + uint32_t id = world.rank() + i * world.size(); + double w = dist(ygm_rng); + items_map[id] = w; + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, items_map, ygm_rng()); + } + } + + // + // Testing the construction of many distributions. Balancing capabilities being tested. + { + uint32_t alias_tables_to_construct = 1000; + uint32_t n_items_per_rank = 1000; + { // Testing uniform weight distribution + std::uniform_int_distribution max_item_weight_dist(50, 100); + for (uint32_t i = 0; i < alias_tables_to_construct; i++) { + // world.cout0("uniform distribution alias table ", i, " of ", alias_tables_to_construct); + ygm::container::map map_of_items(world); + uint32_t max_item_weight = max_item_weight_dist(ygm_rng); + std::uniform_real_distribution weight_dist(0, max_item_weight); + for (uint32_t j = 0; j < n_items_per_rank; j++) { + uint32_t id = world.rank() + j * world.size(); + double w = weight_dist(ygm_rng); + map_of_items.async_insert(id,w); + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, map_of_items); + } + world.cout0("Finished uniform distribution alias table test"); + } + { // Testing normal weight distribution + std::uniform_int_distribution mean_dist(50, 100); + std::uniform_int_distribution std_dev_dist(5, 20); + for (uint32_t i = 0; i < alias_tables_to_construct; i++) { + // world.cout0("Normal distribution alias table ", i, " of ", alias_tables_to_construct); + ygm::container::map map_of_items(world); + uint32_t mean = mean_dist(ygm_rng); + uint32_t std_dev = std_dev_dist(ygm_rng); + std::normal_distribution weight_dist(mean, std_dev); + for (uint32_t j = 0; j < n_items_per_rank; j++) { + uint32_t id = world.rank() + j * world.size(); + double w = weight_dist(ygm_rng); + map_of_items.async_insert(id,w); + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, map_of_items, ygm_rng()); + } + world.cout0("Finished normal distribution alias table test"); + } + { // Testing gamma weight distribution + std::uniform_real_distribution alpha_dist(0.1, 10); + std::uniform_real_distribution theta_dist(10, 100); + for (uint32_t i = 0; i < alias_tables_to_construct; i++) { + // world.cout0("Gamma distribution alias table ", i, " of ", alias_tables_to_construct); + ygm::container::map map_of_items(world); + double alpha = alpha_dist(ygm_rng); + double theta = theta_dist(ygm_rng); + std::gamma_distribution weight_dist(alpha, theta); + for (uint32_t j = 0; j < n_items_per_rank; j++) { + uint32_t id = world.rank() + j * world.size(); + double w = weight_dist(ygm_rng); + map_of_items.async_insert(id,w); + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, map_of_items, ygm_rng()); + } + world.cout0("Finished gamma distribution alias table test"); + } + } + + // + // Test sampling numbers + { + ygm::container::map map_of_items(world); + + uint32_t n_items_per_rank = 1000; + uint32_t max_item_weight = 100; + std::uniform_real_distribution dist(0, max_item_weight); + for (uint32_t i = 0; i < n_items_per_rank; i++) { + uint32_t id = world.rank() + i * world.size(); + double w = dist(ygm_rng); + map_of_items.async_insert(id,w); + } + world.barrier(); + ygm::random::alias_table alias_tbl(world, map_of_items, ygm_rng()); + + static uint32_t samples = 0; + uint32_t samples_per_rank = 100000; + for (uint32_t i = 0; i < samples_per_rank; i++) { + alias_tbl.async_sample([]([[maybe_unused]] auto ptr, [[maybe_unused]] uint32_t item){ + samples++; + }); + } + world.barrier(); + uint32_t total_samples = ygm::sum(samples, world); + YGM_ASSERT_RELEASE(total_samples == (samples_per_rank * world.size())); + } + + // + // Test sampling words with probability proportional to their frequency in a corpus + { + std::vector words; + std::ifstream file("data/loremipsum/loremipsum_0.txt"); + ygm::container::counting_set word_counts(world); + + static std::string ipsum = "ipsum"; + uint32_t ipsum_count = 0; + static std::string sit = "sit"; + uint32_t sit_count = 0; + uint32_t total_words = 0; + if (world.rank0()) { + std::string word; + while (file >> word) { + word_counts.async_insert(word); + ++total_words; + if (word == ipsum) { + ++ipsum_count; + } else if (word == sit) { + ++sit_count; + } + } + } + ygm::random::alias_table alias_tbl(world, word_counts); + world.barrier(); + file.close(); + + static uint64_t samples = 0; + static uint64_t sampled_ipsums = 0; + static uint64_t sampled_sits = 0; + uint32_t samples_per_rank = 10000000; + for (uint32_t i = 0; i < samples_per_rank; i++) { + alias_tbl.async_sample([](std::string word_sample){ + samples++; + if (word_sample == ipsum) { + ++sampled_ipsums; + } else if (word_sample == sit) { + ++sampled_sits; + } + }); + } + world.barrier(); + uint64_t total_samples = ygm::sum(samples, world); + uint64_t total_ipsums = ygm::sum(sampled_ipsums, world); + uint64_t total_sits = ygm::sum(sampled_sits, world); + + YGM_ASSERT_RELEASE(total_samples == (samples_per_rank * world.size())); + + if (world.rank() == 0) { + double ipsum_freq = double(ipsum_count) / total_words; + double sit_freq = double(sit_count) / total_words; + double ipsum_sample_freq = double(total_ipsums) / total_samples; + double sit_sample_freq = double(total_sits) / total_samples; + + world.cout0("\"ipsum\" actual frequency: ", ipsum_freq); + world.cout0("\"ipsum\" sample frequency: ", ipsum_sample_freq); + double dif = std::abs(ipsum_sample_freq - ipsum_freq); + world.cout0("\"ipsum\" frequency difference: ", dif); + YGM_ASSERT_RELEASE(dif < 1e-3); + + world.cout0("\"sit\" actual frequency: ", sit_freq); + world.cout0("\"sit\" sample frequency: ", sit_sample_freq); + dif = std::abs(sit_sample_freq - sit_freq); + world.cout0("\"sit\" frequency difference: ", dif); + YGM_ASSERT_RELEASE(dif < 1e-3); + } + } + + return 0; +} diff --git a/test/test_bag.cpp b/test/test_bag.cpp index 24a2e4e9..fadb595c 100644 --- a/test/test_bag.cpp +++ b/test/test_bag.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include int main(int argc, char** argv) { ygm::comm world(&argc, &argv); @@ -217,12 +217,12 @@ int main(int argc, char** argv) { } } int seed = 100; - ygm::default_random_engine<> rng1 = - ygm::default_random_engine<>(world, seed); + ygm::random::default_random_engine<> rng1 = + ygm::random::default_random_engine<>(world, seed); bbag.local_shuffle(rng1); - ygm::default_random_engine<> rng2 = - ygm::default_random_engine<>(world, seed); + ygm::random::default_random_engine<> rng2 = + ygm::random::default_random_engine<>(world, seed); bbag.global_shuffle(rng2); bbag.local_shuffle(); diff --git a/test/test_gather_topk.cpp b/test/test_gather_topk.cpp index 246be208..11cec38e 100644 --- a/test/test_gather_topk.cpp +++ b/test/test_gather_topk.cpp @@ -11,7 +11,6 @@ #include #include #include -#include int main(int argc, char** argv) { ygm::comm world(&argc, &argv); diff --git a/test/test_multi_compilation_unit_lib.h b/test/test_multi_compilation_unit_lib.h index 9af744d4..fef29903 100644 --- a/test/test_multi_compilation_unit_lib.h +++ b/test/test_multi_compilation_unit_lib.h @@ -1,7 +1,9 @@ // Including all of YGM (except parquet_parser) #include -#include + +#include +#include #include #include diff --git a/test/test_random.cpp b/test/test_random.cpp index 4d88a0c2..d3c21d64 100644 --- a/test/test_random.cpp +++ b/test/test_random.cpp @@ -6,7 +6,7 @@ #undef NDEBUG #include #include -#include +#include #include @@ -17,7 +17,7 @@ int main(int argc, char** argv) { // Test default_random_engine { std::uint32_t seed = 100; - ygm::default_random_engine<> rng(world, seed); + ygm::random::default_random_engine<> rng(world, seed); ygm::container::counting_set seed_set(world); ygm::container::counting_set rn_set(world); ygm::container::counting_set sample_set(world); diff --git a/test/test_reduce.cpp b/test/test_reduce.cpp index a62638b5..55d20cb8 100644 --- a/test/test_reduce.cpp +++ b/test/test_reduce.cpp @@ -11,7 +11,6 @@ #include #include #include -#include int main(int argc, char** argv) { ygm::comm world(&argc, &argv); diff --git a/test/test_transform.cpp b/test/test_transform.cpp index 16c23720..09178983 100644 --- a/test/test_transform.cpp +++ b/test/test_transform.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include int main(int argc, char** argv) { ygm::comm world(&argc, &argv);