diff --git a/environment/cmake/LueConfiguration.cmake b/environment/cmake/LueConfiguration.cmake index b2b53db3..73a84be9 100644 --- a/environment/cmake/LueConfiguration.cmake +++ b/environment/cmake/LueConfiguration.cmake @@ -158,6 +158,7 @@ set(LUE_TEMPLATIZE "${PROJECT_SOURCE_DIR}/environment/script/templatize.py") # NOTE These can be made configurable later on set(LUE_FRAMEWORK_CONDITION_ELEMENT uint8_t) set(LUE_FRAMEWORK_BOOLEAN_ELEMENT uint8_t) +set(LUE_FRAMEWORK_INDEX_ELEMENT uint64_t) set(LUE_FRAMEWORK_FLOW_DIRECTION_ELEMENT uint8_t) set(LUE_FRAMEWORK_SIGNED_INTEGRAL_ELEMENTS int32_t int64_t) set(LUE_FRAMEWORK_UNSIGNED_INTEGRAL_ELEMENTS uint8_t uint32_t uint64_t) diff --git a/source/framework/algorithm/CMakeLists.txt b/source/framework/algorithm/CMakeLists.txt index 370dc0a5..2063605c 100644 --- a/source/framework/algorithm/CMakeLists.txt +++ b/source/framework/algorithm/CMakeLists.txt @@ -529,6 +529,34 @@ block() set(generated_source_files ${generated_source_files} PARENT_SCOPE) endblock() +block() + set(count "0") + + set(IndexElement ${LUE_FRAMEWORK_INDEX_ELEMENT}) + set(ConditionElement ${LUE_FRAMEWORK_BOOLEAN_ELEMENT}) + + foreach(Policies IN LISTS LUE_FRAMEWORK_POLICIES) + foreach(rank IN LISTS ranks) + math(EXPR count "${count} + 1") + + # Instantiate cell_index + set(output_pathname "${CMAKE_CURRENT_BINARY_DIR}/${offset}/cell_index-${count}.cpp") + + generate_template_instantiation( + INPUT_PATHNAME + "${CMAKE_CURRENT_SOURCE_DIR}/${offset}/cell_index.cpp.in" + OUTPUT_PATHNAME + "${output_pathname}" + DICTIONARY + '{"Policies":"${Policies}","IndexElement":"${IndexElement}","ConditionElement":"${ConditionElement}","rank":"${rank}"}' + ) + list(APPEND generated_source_files "${output_pathname}") + endforeach() + endforeach() + + set(generated_source_files ${generated_source_files} PARENT_SCOPE) +endblock() + block() set(count "0") diff --git a/source/framework/algorithm/include/lue/framework/algorithm/cell_index.hpp b/source/framework/algorithm/include/lue/framework/algorithm/cell_index.hpp new file mode 100644 index 00000000..58172ea8 --- /dev/null +++ b/source/framework/algorithm/include/lue/framework/algorithm/cell_index.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "lue/framework/algorithm/policy.hpp" +#include "lue/framework/partitioned_array.hpp" + + +namespace lue { + + template + auto cell_index( + Policies const& policies, + PartitionedArray, rank> const& condition, + Index dimension_idx) -> PartitionedArray, rank>; + +} // namespace lue diff --git a/source/framework/algorithm/include/lue/framework/algorithm/default_policies/cell_index.hpp b/source/framework/algorithm/include/lue/framework/algorithm/default_policies/cell_index.hpp new file mode 100644 index 00000000..21f8f458 --- /dev/null +++ b/source/framework/algorithm/include/lue/framework/algorithm/default_policies/cell_index.hpp @@ -0,0 +1,29 @@ +#pragma once +#include "lue/framework/algorithm/cell_index.hpp" + + +namespace lue { + namespace policy::cell_index { + + template + using DefaultPolicies = policy::DefaultPolicies< + AllValuesWithinDomain, + OutputElements, + InputElements>; + + } // namespace policy::cell_index + + + namespace default_policies { + + template + auto cell_index(PartitionedArray const& condition, Index const dimension_idx) + -> PartitionedArray + { + using Policies = policy::cell_index::DefaultPolicies; + + return cell_index(Policies{}, condition, dimension_idx); + } + + } // namespace default_policies +} // namespace lue diff --git a/source/framework/algorithm/include/lue/framework/algorithm/definition/cell_index.hpp b/source/framework/algorithm/include/lue/framework/algorithm/definition/cell_index.hpp new file mode 100644 index 00000000..6c872789 --- /dev/null +++ b/source/framework/algorithm/include/lue/framework/algorithm/definition/cell_index.hpp @@ -0,0 +1,526 @@ +#pragma once +#include "lue/framework/algorithm/cell_index.hpp" +// #include "lue/framework/algorithm/definition/unary_local_operation.hpp" +#include "lue/framework/algorithm/local_operation_export.hpp" +#include "lue/macro.hpp" + + +namespace lue { + // namespace detail::first_n { + // + // using Data = CellCounter; + // + // template + // class Walk; + // + // namespace server { + // + // template + // class Walk: public hpx::components::component_base> + // { + // + // private: + // + // using ComponentServerBase = hpx::components::component_base>; + // using ComponentClient = first_n::Walk; + // using OutputElement = policy::OutputElementT; + // using RouteID = policy::InputElementT; + // + // protected: + // + // using OutputArray = PartitionedArray; + // using Offset = OffsetT; + // using Shape = ShapeT; + // + // public: + // + // using OutputPartition = PartitionT; + // using RoutePartition = lue::SerialRoutePartition; + // + // + // Walk(Policies const& policies, RoutePartition route_partition): + // + // ComponentServerBase{}, + // _policies{policies}, + // _output_partition_promise{}, + // _output_partition{}, + // _route_partition{std::move(route_partition)}, + // _walk_mutex{}, + // _downstream_components{}, + // _fragment_idxs{} + // + // { + // using OutputPartitionServer = typename OutputPartition::Server; + // + // auto const& ondp = + // std::get<0>(_policies.outputs_policies()).output_no_data_policy(); + // OutputElement no_data_value; + // + // ondp.mark_no_data(no_data_value); + // + // _route_partition.wait(); + // auto route_partition_ptr{ready_component_ptr(_route_partition)}; + // + // _output_partition = OutputPartition{hpx::new_( + // hpx::find_here(), + // route_partition_ptr->offset(), + // route_partition_ptr->shape(), + // no_data_value)}; + // + // std::set const route_ids{route_partition_ptr->route_ids()}; + // + // for (RouteID const route_id : route_ids) + // { + // // Initialize with idx. First fragment to visit has index + // // zero. This index increases during the walk, until the last + // // fragment has been handled, after which the index for this + // // route_id can be removed. When all indices have been removed, + // // the walk has finished. + // _fragment_idxs[route_id] = 0; + // } + // } + // + // + // OutputPartition result_partition() + // { + // // Connect the promise with the future + // // This function must be called exactly once + // OutputPartition partition{_output_partition_promise.get_future()}; + // + // // If the route partition contains no fragments, the output partition + // // can be marked as finished immediately + // if (_fragment_idxs.empty()) + // { + // finish_partition(); + // } + // + // return partition; + // } + // + // + // void set_downstream_components(std::map&& + // components) + // { + // _downstream_components = components; + // } + // + // + // void walk(RouteID const route_id, Data&& data) + // { + // // This function may be called by multiple threads, for different + // // routes, or for the same route but for different fragments. In + // // the latter case, these calls will be ordered, with the first + // // call handling an upstream fragment and subsequent calls handling + // // downstream fragments. + // + // // std::shared_lock read_lock{_walk_mutex, std::defer_lock}; + // std::unique_lock write_lock{_walk_mutex, std::defer_lock}; + // + // // First, do stuff, *using* state veriables. Don't change stuff that + // // is shared between threads. Changing different cells in a raster + // // is fine, just don't change the same values. + // // TODO read_lock.lock(); + // write_lock.lock(); + // + // auto route_partition_server_ptr{ready_component_ptr(_route_partition)}; + // auto& route_partition_server{*route_partition_server_ptr}; + // + // auto output_partition_server_ptr{ready_component_ptr(_output_partition)}; + // auto& output_partition_server{*output_partition_server_ptr}; + // auto output_partition_data{output_partition_server.data()}; + // + // lue_hpx_assert( + // route_partition_server.route_ids().find(route_id) != + // route_partition_server.route_ids().end()); + // + // lue_hpx_assert(!_fragment_idxs.empty()); + // lue_hpx_assert(_fragment_idxs.find(route_id) != _fragment_idxs.end()); + // + // Index& fragment_idx{_fragment_idxs.at(route_id)}; + // + // auto const& route_fragments{route_partition_server.route_fragments(route_id)}; + // + // lue_hpx_assert(fragment_idx < static_cast(std::size(route_fragments))); + // + // auto const& route_fragment{route_fragments[fragment_idx]}; + // + // auto const& cell_idxs{route_fragment.cell_idxs()}; + // + // // For as long as needed, iterate over each cell in the current + // // fragment. For each cell: + // // - Calculate an output value + // // - Assign output value to corresponding cell in the output partition + // for (std::size_t idx = 0; idx < cell_idxs.size() && data.keep_walking(); + // ++idx, ++data) + // { + // output_partition_data[cell_idxs[idx]] = 1; + // } + // + // // TODO read_lock.unlock(); + // // TODO write_lock.lock(); + // + // ++fragment_idx; + // + // if (!route_fragment.is_last()) + // { + // if (data.keep_walking()) + // { + // // Continue the walk in a downstream partition + // lue_hpx_assert(route_fragment.next_fragment_location().valid()); + // lue_hpx_assert(route_fragment.next_fragment_location().is_ready()); + // + // hpx::id_type const downstream_route_partition_id{ + // route_fragment.next_fragment_location().get()}; + // + // lue_hpx_assert( + // _downstream_components.find(downstream_route_partition_id) != + // _downstream_components.end()); + // + // _downstream_components.at(downstream_route_partition_id) + // .walk(route_id, std::move(data)); + // } + // else + // { + // lue_hpx_assert(route_fragment.next_fragment_location().valid()); + // lue_hpx_assert(route_fragment.next_fragment_location().is_ready()); + // + // hpx::id_type const downstream_route_partition_id{ + // route_fragment.next_fragment_location().get()}; + // + // lue_hpx_assert( + // _downstream_components.find(downstream_route_partition_id) != + // _downstream_components.end()); + // + // _downstream_components.at(downstream_route_partition_id) + // .skip_walking_route_fragments(route_id); + // } + // } + // + // lue_hpx_assert( + // (route_fragment.is_last() && + // fragment_idx == static_cast(std::size(route_fragments))) || + // !route_fragment.is_last()); + // + // if (fragment_idx == static_cast(std::size(route_fragments))) + // { + // // This was the last fragment of the route that is located in + // // this partition. If it was the last fragment at all, we can / must + // // finish the partition. + // _fragment_idxs.erase(route_id); + // + // if (_fragment_idxs.empty()) + // { + // finish_partition(); + // } + // } + // } + // + // + // void skip_walking_route_fragments(RouteID const route_id) + // { + // // Skip walking route fragments for the route with the ID passed in. + // // If this results in an empty _fragment_idxs collection, then + // // apparently we have handled all fragments of all routes, and the + // // output partition can be finished. + // + // // This function is called from another component. Since we will + // // be changing the state of the current component, we need to obtain + // // a write lock. + // + // // TODO Had to guard the whole region by a write block. Apparently, + // // threads started tripping over each other. For some reason, + // // the route_id passed in wasn't in _fragment_idxs anymore, which + // // must never be the case. + // + // // TODO std::shared_lock read_lock{_walk_mutex, std::defer_lock}; + // std::unique_lock write_lock{_walk_mutex, std::defer_lock}; + // + // // TODO read_lock.lock(); + // write_lock.lock(); + // + // lue_hpx_assert(!_fragment_idxs.empty()); + // lue_hpx_assert(_fragment_idxs.find(route_id) != _fragment_idxs.end()); + // + // Index& fragment_idx{_fragment_idxs.at(route_id)}; + // + // auto const& route_fragments{ + // ready_component_ptr(_route_partition)->route_fragments(route_id)}; + // lue_hpx_assert(fragment_idx < static_cast(std::size(route_fragments))); + // + // auto const& route_fragment{route_fragments[fragment_idx]}; + // + // if (!route_fragment.is_last()) + // { + // lue_hpx_assert(route_fragment.next_fragment_location().valid()); + // lue_hpx_assert(route_fragment.next_fragment_location().is_ready()); + // + // hpx::id_type const downstream_route_partition_id{ + // route_fragment.next_fragment_location().get()}; + // + // lue_hpx_assert( + // _downstream_components.find(downstream_route_partition_id) != + // _downstream_components.end()); + // + // _downstream_components.at(downstream_route_partition_id) + // .skip_walking_route_fragments(route_id); + // } + // + // // TODO read_lock.unlock(); + // // TODO write_lock.lock(); + // + // ++fragment_idx; + // + // if (fragment_idx == static_cast(std::size(route_fragments))) + // { + // // This was the last fragment of the route that is located in + // // this partition. If it was the last fragment at all, we can / must + // // finish the partition. + // _fragment_idxs.erase(route_id); + // + // if (_fragment_idxs.empty()) + // { + // finish_partition(); + // } + // } + // } + // + // + // HPX_DEFINE_COMPONENT_ACTION(Walk, result_partition, ResultPartitionAction); + // + // HPX_DEFINE_COMPONENT_ACTION( + // Walk, set_downstream_components, SetDownstreamComponentsAction); + // + // HPX_DEFINE_COMPONENT_ACTION(Walk, walk, WalkAction); + // + // HPX_DEFINE_COMPONENT_ACTION( + // Walk, skip_walking_route_fragments, SkipWalkingRouteFragmentsAction); + // + // private: + // + // void finish_partition() + // { + // // This function must be called exactly once + // lue_hpx_assert(_output_partition.valid()); + // lue_hpx_assert(_output_partition.is_ready()); + // lue_hpx_assert(_fragment_idxs.empty()); + // + // _output_partition_promise.set_value(_output_partition.get_id()); + // + // _downstream_components.clear(); + // } + // + // Policies _policies; + // + // mutable hpx::promise _output_partition_promise; + // + // OutputPartition _output_partition; + // + // RoutePartition _route_partition; + // + // hpx::shared_mutex _walk_mutex; + // + // //! Component client by route partition ID + // std::map _downstream_components; + // + // //! Route fragment index by route ID + // std::map _fragment_idxs; + // }; + // + // } // namespace server + // + // + // template + // class Walk: public hpx::components::client_base, server::Walk> + // { + // + // private: + // + // using ComponentServer = server::Walk; + // using ComponentClientBase = + // hpx::components::client_base, ComponentServer>; + // using OutputPartition = typename ComponentServer::OutputPartition; + // + // public: + // + // Walk(): + // + // ComponentClientBase{} + // + // { + // } + // + // + // Walk(hpx::id_type const component_id): + // + // ComponentClientBase{component_id} + // + // { + // } + // + // + // Walk(hpx::future&& component_id): + // + // ComponentClientBase{std::move(component_id)} + // + // { + // } + // + // + // Walk(Walk const&) = default; + // + // Walk(Walk&&) = default; + // + // ~Walk() = default; + // + // Walk& operator=(Walk const&) = default; + // + // Walk& operator=(Walk&&) = default; + // + // + // OutputPartition result_partition() const + // { + // lue_hpx_assert(this->is_ready()); + // lue_hpx_assert(this->get_id()); + // + // typename ComponentServer::ResultPartitionAction action; + // + // return hpx::async(action, this->get_id()); + // } + // + // + // hpx::future set_downstream_components(std::map&& components) + // { + // lue_hpx_assert(this->is_ready()); + // lue_hpx_assert(this->get_id()); + // + // typename ComponentServer::SetDownstreamComponentsAction action; + // + // return hpx::async(action, this->get_id(), std::move(components)); + // } + // + // + // hpx::future walk(RouteID const route_id, Data data) const + // { + // lue_hpx_assert(this->is_ready()); + // lue_hpx_assert(this->get_id()); + // + // typename ComponentServer::WalkAction action; + // + // return hpx::async(action, this->get_id(), route_id, std::move(data)); + // } + // + // + // hpx::future skip_walking_route_fragments(RouteID const route_id) const + // { + // lue_hpx_assert(this->is_ready()); + // lue_hpx_assert(this->get_id()); + // + // typename ComponentServer::SkipWalkingRouteFragmentsAction action; + // + // return hpx::async(action, this->get_id(), route_id); + // } + // }; + // + // } // namespace detail::first_n + // + // + // template + // auto first_n( + // Policies const& policies, + // SerialRoute, rank> const& route, + // Count const max_nr_cells) -> PartitionedArray, rank> + // { + // static_assert(std::is_integral_v>); + // static_assert(std::is_integral_v>); + // + // using RouteID = policy::InputElementT; + // using Route = SerialRoute; + // using RoutePartition = PartitionT2; + // + // using OutputElement = policy::OutputElementT; + // using OutputArray = PartitionedArray; + // using OutputPartition = PartitionT; + // using OutputPartitions = PartitionsT; + // + // // Iterate over all route partitions and: + // // - Create a new Walk component, passing in the route partition + // + // using WalkComponentClient = detail::first_n::Walk; + // using WalkComponentServer = detail::first_n::server::Walk; + // + // // TODO + // // Since a partioned array currently caches locality IDs, we need to retrieve them, + // // unfortunately. The localities stuff can be removed once arrays don't cache locality + // // IDs anymore. + // + // auto const& route_partitions{route.partitions()}; + // + // Array, rank> locality_fs{detail::localities(route_partitions)}; + // + // Count const nr_partitions{nr_elements(route_partitions.shape())}; + // Array walk_components{route_partitions.shape()}; + // + // for (Index partition_idx = 0; partition_idx < nr_partitions; ++partition_idx) + // { + // walk_components[partition_idx] = WalkComponentClient{hpx::dataflow( + // hpx::launch::async, + // [policies](RoutePartition const& route_partition) + // { + // return hpx::new_( + // hpx::colocated(route_partition.get_id()), policies, route_partition); + // }, + // route_partitions[partition_idx])}; + // } + // + // OutputPartitions partitions{walk_components.shape()}; + // + // for (Index partition_idx = 0; partition_idx < nr_partitions; ++partition_idx) + // { + // partitions[partition_idx] = walk_components[partition_idx].then( + // hpx::unwrapping([](WalkComponentClient const& component) + // { return OutputPartition{component.result_partition()}; })); + // } + // + // detail::first_n::Data data{max_nr_cells}; + // + // // Do whatever it takes to end up with ready output partitions + // walk(route, walk_components, std::move(data)); + // + // // Keep the components alive for as long as the partitions are not ready + // hpx::when_all(partitions.begin(), partitions.end()) + // .then([components = std::move(walk_components)](auto&&) { HPX_UNUSED(components); }); + // + // // TODO Get rid of this wait and localities stuff + // Array localities{locality_fs.shape()}; + // { + // hpx::wait_all(locality_fs.begin(), locality_fs.end()); + // std::transform( + // locality_fs.begin(), + // locality_fs.end(), + // localities.begin(), + // [](hpx::future& locality_f) { return locality_f.get(); }); + // } + // + // return OutputArray{route.shape(), std::move(localities), std::move(partitions)}; + // } + + + template + auto cell_index( + Policies const& policies, + PartitionedArray, rank> const& condition, + Index dimension_idx) -> PartitionedArray, rank> + { + } + +} // namespace lue + + +#define LUE_INSTANTIATE_CELL_INDEX(Policies, rank) \ + \ + template LUE_LOCAL_OPERATION_EXPORT PartitionedArray, rank> \ + cell_index, rank>( \ + ArgumentType const&, \ + PartitionedArray, rank> const&, \ + Index const); diff --git a/source/framework/algorithm/include/lue/framework/algorithm/value_policies/cell_index.hpp b/source/framework/algorithm/include/lue/framework/algorithm/value_policies/cell_index.hpp new file mode 100644 index 00000000..e544ee0b --- /dev/null +++ b/source/framework/algorithm/include/lue/framework/algorithm/value_policies/cell_index.hpp @@ -0,0 +1,29 @@ +#pragma once +#include "lue/framework/algorithm/cell_index.hpp" + + +namespace lue { + namespace policy::cell_index { + + template + using DefaultValuePolicies = policy::DefaultValuePolicies< + AllValuesWithinDomain, + OutputElements, + InputElements>; + + } // namespace policy::cell_index + + + namespace value_policies { + + template + auto cell_index(PartitionedArray const& condition, Index const dimension_idx) + -> PartitionedArray + { + using Policies = policy::cell_index::DefaultValuePolicies; + + return cell_index(Policies{}, condition, dimension_idx); + } + + } // namespace value_policies +} // namespace lue diff --git a/source/framework/algorithm/src/local_operation/cell_index.cpp.in b/source/framework/algorithm/src/local_operation/cell_index.cpp.in new file mode 100644 index 00000000..00b111a2 --- /dev/null +++ b/source/framework/algorithm/src/local_operation/cell_index.cpp.in @@ -0,0 +1,13 @@ +#include "lue/framework/algorithm/default_policies/cell_index.hpp" +#include "lue/framework/algorithm/definition/cell_index.hpp" +#include "lue/framework/algorithm/value_policies/cell_index.hpp" + + +namespace lue { + + LUE_INSTANTIATE_CELL_INDEX( + ESC(policy::cell_index::{{ Policies }}<{{ IndexElement }}, {{ ConditionElement }}>), + {{ rank }} + ); + +} // namespace lue diff --git a/source/framework/python/CMakeLists.txt b/source/framework/python/CMakeLists.txt index 8f846ee5..352b744d 100644 --- a/source/framework/python/CMakeLists.txt +++ b/source/framework/python/CMakeLists.txt @@ -91,6 +91,7 @@ add_library(lue_py_framework SHARED src/algorithm/local_operation.cpp src/algorithm/local_operation/ceil.cpp + src/algorithm/local_operation/cell_index.cpp src/algorithm/local_operation/floor.cpp src/algorithm/local_operation/log10.cpp src/algorithm/local_operation/logical_and.cpp diff --git a/source/framework/python/src/algorithm/local_operation.cpp b/source/framework/python/src/algorithm/local_operation.cpp index 18cf13d4..9f0dab1f 100644 --- a/source/framework/python/src/algorithm/local_operation.cpp +++ b/source/framework/python/src/algorithm/local_operation.cpp @@ -25,6 +25,7 @@ namespace lue::framework { void bind_cast(pybind11::module& module); void bind_cos(pybind11::module& module); void bind_ceil(pybind11::module& module); + void bind_cell_index(pybind11::module& module); void bind_d8_flow_direction(pybind11::module& module); void bind_divide(pybind11::module& module); void bind_downstream(pybind11::module& module); @@ -87,6 +88,7 @@ namespace lue::framework { bind_atan2(module); bind_cast(module); bind_ceil(module); + bind_cell_index(module); bind_cos(module); bind_d8_flow_direction(module); bind_divide(module); diff --git a/source/framework/python/src/algorithm/local_operation/cell_index.cpp b/source/framework/python/src/algorithm/local_operation/cell_index.cpp new file mode 100644 index 00000000..620b0991 --- /dev/null +++ b/source/framework/python/src/algorithm/local_operation/cell_index.cpp @@ -0,0 +1,26 @@ +#include "lue/framework/algorithm/value_policies/cell_index.hpp" +#include + + +namespace lue::framework { + namespace { + + using ConditionElement = std::uint8_t; + using IndexElement = std::uint64_t; + + template + auto cell_index(PartitionedArray const& condition, Index const dimension_idx) + -> PartitionedArray + { + return value_policies::cell_index(condition, dimension_idx); + } + + } // Anonymous namespace + + + void bind_cell_index(pybind11::module& module) + { + module.def("cell_idx", cell_index<2>); + } + +} // namespace lue::framework diff --git a/source/framework/python/test/algorithm/local_operation/cell_index_test.py b/source/framework/python/test/algorithm/local_operation/cell_index_test.py new file mode 100644 index 00000000..e138d438 --- /dev/null +++ b/source/framework/python/test/algorithm/local_operation/cell_index_test.py @@ -0,0 +1,20 @@ +import numpy as np + +import lue.framework as lfr +import lue_test + + +def setUpModule(): + lue_test.start_hpx_runtime() + + +def tearDownModule(): + lue_test.stop_hpx_runtime() + + +class CellIndexTest(lue_test.TestCase): + @lue_test.framework_test_case + def test_overloads(self): + array_shape = (60, 40) + condition = lfr.create_array(array_shape, np.uint8, 1) + lfr.cell_index(condition, 0)