From 6340bd45022a2e0fda8a0213b269a02880cf82b1 Mon Sep 17 00:00:00 2001 From: "Yu-Hsiang M. Tsai" Date: Wed, 9 Aug 2023 16:55:02 +0200 Subject: [PATCH 01/25] try --- core/CMakeLists.txt | 1 + core/config/config.cpp | 63 ++++++++ core/solver/cg.cpp | 35 +++++ core/test/config/CMakeLists.txt | 1 + core/test/config/config.cpp | 157 ++++++++++++++++++++ include/ginkgo/core/config/config.hpp | 74 +++++++++ include/ginkgo/core/config/registry.hpp | 190 ++++++++++++++++++++++++ include/ginkgo/core/solver/cg.hpp | 17 +++ 8 files changed, 538 insertions(+) create mode 100644 core/config/config.cpp create mode 100644 core/test/config/config.cpp create mode 100644 include/ginkgo/core/config/config.hpp create mode 100644 include/ginkgo/core/config/registry.hpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index ac15a869e79..a8142f1c72d 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -19,6 +19,7 @@ target_sources(ginkgo base/segmented_array.cpp base/timer.cpp base/version.cpp + config/config.cpp config/property_tree.cpp distributed/index_map.cpp distributed/partition.cpp diff --git a/core/config/config.cpp b/core/config/config.cpp new file mode 100644 index 00000000000..667ffc51e6a --- /dev/null +++ b/core/config/config.cpp @@ -0,0 +1,63 @@ +/************************************************************* +Copyright (c) 2017-2023, the Ginkgo authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************/ + +#include + + +#include + + +namespace gko { +namespace config { + + +BuildFromConfigMap generate_config_map() +{ + return {{"Cg", build_from_config(LinOpFactoryType::Cg)>}}; +} + + +std::unique_ptr build_from_config( + const gko::config::Config& config, std::shared_ptr& exec, + const gko::config::registry& context) +{ + auto type = config.find("Type"); + if (type != config.end()) { + auto func = context.get_build_map().at(type->second); + return func(config, exec, context); + } + return nullptr; +} + + +} // namespace config +} // namespace gko diff --git a/core/solver/cg.cpp b/core/solver/cg.cpp index e445cfcafaf..7d1821e19e0 100644 --- a/core/solver/cg.cpp +++ b/core/solver/cg.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "core/distributed/helpers.hpp" @@ -20,6 +22,39 @@ namespace gko { +namespace config { + + +template <> +std::unique_ptr +build_from_config(gko::config::LinOpFactoryType::Cg)>( + const gko::config::Config& config, std::shared_ptr& exec, + const gko::config::registry& context) +{ + // TODO: select the type, always using double as demo. + // extract the following to another function (or build_from_config) to make + // this function only select type + auto factory = gko::solver::Cg::build_from_config(config, context); + // handle parameter requires exec + // criteria and preconditioner are almost in each solver -> to another + // function. + factory.with_criteria( + gko::stop::Iteration::build().with_max_iters(1u).on(exec)); + { + auto str = config.find("preconditioner"); + if (str != config.end()) { + // assume we have the config for nest object + factory.with_preconditioner(build_from_config( + Config{{"Type", str->second}}, exec, context)); + } + } + return std::move(factory.on(exec)); +} + + +} // namespace config + + namespace solver { namespace cg { namespace { diff --git a/core/test/config/CMakeLists.txt b/core/test/config/CMakeLists.txt index e842152634c..95ad05582cb 100644 --- a/core/test/config/CMakeLists.txt +++ b/core/test/config/CMakeLists.txt @@ -1 +1,2 @@ +ginkgo_create_test(config) ginkgo_create_test(property_tree) diff --git a/core/test/config/config.cpp b/core/test/config/config.cpp new file mode 100644 index 00000000000..8fa0df62fd2 --- /dev/null +++ b/core/test/config/config.cpp @@ -0,0 +1,157 @@ +/************************************************************* +Copyright (c) 2017-2023, the Ginkgo authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************/ + +#include + + +#include + + +#include + + +#include +#include +#include +#include +#include +#include +#include + + +#include "core/test/utils.hpp" + + +namespace { + + +template +class Config : public ::testing::Test { +protected: + using value_type = double; + using Mtx = gko::matrix::Dense; + Config() + : exec(gko::ReferenceExecutor::create()), + mtx(gko::initialize( + {{2, -1.0, 0.0}, {-1.0, 2, -1.0}, {0.0, -1.0, 2}}, exec)) + {} + + std::shared_ptr exec; + std::shared_ptr mtx; +}; + +TYPED_TEST_SUITE(Config, gko::test::ValueTypes, TypenameNameGenerator); + + +TYPED_TEST(Config, GenerateMap) +{ + ASSERT_NO_THROW(gko::config::generate_config_map()); +} + + +TYPED_TEST(Config, GenerateObject) +{ + auto config_map = gko::config::generate_config_map(); + auto reg = gko::config::registry(config_map); + + auto obj = gko::config::build_from_config<0>(gko::config::Config{}, + this->exec, reg); + + ASSERT_NE(dynamic_cast::Factory*>(obj.get()), + nullptr); +} + + +TYPED_TEST(Config, GenerateObjectWithData) +{ + auto config_map = gko::config::generate_config_map(); + auto reg = gko::config::registry(config_map); + reg.emplace("precond", this->mtx); + + auto obj = gko::config::build_from_config<0>( + gko::config::Config{{"generated_preconditioner", "precond"}}, + this->exec, reg); + + ASSERT_NE(dynamic_cast::Factory*>(obj.get()), + nullptr); + ASSERT_NE(dynamic_cast::Factory*>(obj.get()) + ->get_parameters() + .generated_preconditioner, + nullptr); +} + + +TYPED_TEST(Config, GenerateObjectWithPreconditioner) +{ + auto config_map = gko::config::generate_config_map(); + auto reg = gko::config::registry(config_map); + + auto obj = gko::config::build_from_config<0>( + gko::config::Config{{"preconditioner", "Cg"}}, this->exec, reg); + + ASSERT_NE(dynamic_cast::Factory*>(obj.get()), + nullptr); + ASSERT_NE(dynamic_cast::Factory*>(obj.get()) + ->get_parameters() + .preconditioner, + nullptr); +} + + +TYPED_TEST(Config, GenerateObjectWithCustomBuild) +{ + auto config_map = gko::config::generate_config_map(); + + config_map["Custom"] = [](const gko::config::Config& config, + std::shared_ptr& exec, + const gko::config::registry& context) { + return gko::solver::Bicg::build() + .with_criteria( + gko::stop::Iteration::build().with_max_iters(2u).on(exec)) + .on(exec); + }; + auto reg = gko::config::registry(config_map); + + auto obj = gko::config::build_from_config<0>( + gko::config::Config{{"preconditioner", "Custom"}}, this->exec, reg); + + ASSERT_NE(dynamic_cast::Factory*>(obj.get()), + nullptr); + ASSERT_NE(dynamic_cast::Factory*>( + dynamic_cast::Factory*>(obj.get()) + ->get_parameters() + .preconditioner.get()), + nullptr); +} + + +} // namespace diff --git a/include/ginkgo/core/config/config.hpp b/include/ginkgo/core/config/config.hpp new file mode 100644 index 00000000000..d8a07a5e914 --- /dev/null +++ b/include/ginkgo/core/config/config.hpp @@ -0,0 +1,74 @@ +/************************************************************* +Copyright (c) 2017-2023, the Ginkgo authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************/ + +#ifndef GKO_PUBLIC_CORE_CONFIG_CONFIG_HPP_ +#define GKO_PUBLIC_CORE_CONFIG_CONFIG_HPP_ + + +#include +#include +#include + + +#include +#include +#include + + +namespace gko { +namespace config { + + +enum LinOpFactoryType : int { Cg = 0 }; + + +// It is only an intermediate step. If we do not provide the SolverType with VT, +// IT selection, we do not need it in public. +template +std::unique_ptr build_from_config( + const gko::config::Config& config, std::shared_ptr& exec, + const gko::config::registry& context); + +// The main function +std::unique_ptr build_from_config( + const gko::config::Config& config, std::shared_ptr& exec, + const gko::config::registry& context); + + +BuildFromConfigMap generate_config_map(); + + +} // namespace config +} // namespace gko + + +#endif // GKO_PUBLIC_CORE_CONFIG_CONFIG_HPP_ diff --git a/include/ginkgo/core/config/registry.hpp b/include/ginkgo/core/config/registry.hpp new file mode 100644 index 00000000000..9db9122f24e --- /dev/null +++ b/include/ginkgo/core/config/registry.hpp @@ -0,0 +1,190 @@ +/************************************************************* +Copyright (c) 2017-2023, the Ginkgo authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************/ + +#ifndef GKO_PUBLIC_CORE_CONFIG_REGISTRY_HPP_ +#define GKO_PUBLIC_CORE_CONFIG_REGISTRY_HPP_ + + +#include +#include +#include + + +#include +#include + + +namespace gko { +namespace config { + + +using LinOpMap = std::unordered_map>; +using LinOpFactoryMap = + std::unordered_map>; +using Config = std::map; +class registry; +using BuildFunctionType = std::function( + const Config&, std::shared_ptr&, const registry&)>; +using BuildFromConfigMap = std::map; + +/** + * map_type gives the map type according to the base type of given type. + * + * @tparam T the type + */ +template +struct map_type { + using type = void; +}; + +template +struct map_type< + T, typename std::enable_if::value>::type> { + using type = LinOpMap; +}; + +template +struct map_type::value>::type> { + using type = LinOpFactoryMap; +}; + + +class registry { +public: + registry(BuildFromConfigMap build_map) : build_map_(build_map) {} + + /** + * insert_data stores the data with the key. + * + * @tparam T the type + * + * @param key the unique key string + * @param data the shared pointer of the object + */ + template + void emplace(std::string key, std::shared_ptr data) + { + this->get_map().emplace(key, data); + } + + /** + * search_data searches the key on the corresponding map. + * + * @tparam T the type + * + * @param key the key string + * + * @return the shared pointer of the object + */ + template + std::shared_ptr search_data(std::string key) const + { + auto idx = this->get_map().find(key); + if (idx != this->get_map().end()) { + return std::dynamic_pointer_cast(idx->second); + } + // or throw the error + return nullptr; + } + + const BuildFromConfigMap& get_build_map() const { return build_map_; } + + /** + * get_map gets the member map + * + * @tparam T the type + * + * @return the map + */ + template + typename map_type::type& get_map() + { + return this->get_map_impl::type>(); + } + + template + const typename map_type::type& get_map() const + { + return this->get_map_impl::type>(); + } + +protected: + /** + * get_map_impl is the implementation of get_map + * + * @tparam T the map type + * + * @return the map + */ + template + T& get_map_impl(); + + template + const T& get_map_impl() const; + +private: + LinOpMap linop_map_; + LinOpFactoryMap linopfactory_map_; + BuildFromConfigMap build_map_; +}; + + +template <> +inline LinOpMap& registry::get_map_impl() +{ + return linop_map_; +} + +template <> +inline LinOpFactoryMap& registry::get_map_impl() +{ + return linopfactory_map_; +} + +template <> +inline const LinOpMap& registry::get_map_impl() const +{ + return linop_map_; +} + +template <> +inline const LinOpFactoryMap& registry::get_map_impl() const +{ + return linopfactory_map_; +} + + +} // namespace config +} // namespace gko + +#endif // GKO_PUBLIC_CORE_CONFIG_REGISTRY_HPP_ diff --git a/include/ginkgo/core/solver/cg.hpp b/include/ginkgo/core/solver/cg.hpp index a56e543d5ca..c337516a9a0 100644 --- a/include/ginkgo/core/solver/cg.hpp +++ b/include/ginkgo/core/solver/cg.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,22 @@ class Cg : public EnableLinOp>, GKO_ENABLE_LIN_OP_FACTORY(Cg, parameters, Factory); GKO_ENABLE_BUILD_METHOD(Factory); + static auto build_from_config(const gko::config::Config& config, + const gko::config::registry& context) + -> decltype(Factory::create()) + { + auto factory = Factory::create(); + { + auto str = config.find("generated_preconditioner"); + if (str != config.end()) { + auto linop = context.search_data(str->second); + factory.with_generated_preconditioner(linop); + } + } + // can also handle preconditioner, criterion here if they are in + // context. + return factory; + } protected: void apply_impl(const LinOp* b, LinOp* x) const override; From 70f098e6904bcedcf7abea1c159a3303c81eb72d Mon Sep 17 00:00:00 2001 From: "Yu-Hsiang M. Tsai" Date: Thu, 10 Aug 2023 16:16:48 +0200 Subject: [PATCH 02/25] change the order and put it into protected --- core/config/config.cpp | 8 ++-- core/solver/cg.cpp | 60 +++++++++++++++++++------ core/test/config/config.cpp | 16 +++---- include/ginkgo/core/config/config.hpp | 8 ++-- include/ginkgo/core/config/registry.hpp | 2 +- include/ginkgo/core/solver/cg.hpp | 25 ++++------- 6 files changed, 72 insertions(+), 47 deletions(-) diff --git a/core/config/config.cpp b/core/config/config.cpp index 667ffc51e6a..412ee2240ca 100644 --- a/core/config/config.cpp +++ b/core/config/config.cpp @@ -42,18 +42,18 @@ namespace config { BuildFromConfigMap generate_config_map() { - return {{"Cg", build_from_config(LinOpFactoryType::Cg)>}}; + return {{"Cg", build_from_config}}; } std::unique_ptr build_from_config( - const gko::config::Config& config, std::shared_ptr& exec, - const gko::config::registry& context) + const gko::config::Config& config, const gko::config::registry& context, + std::shared_ptr& exec) { auto type = config.find("Type"); if (type != config.end()) { auto func = context.get_build_map().at(type->second); - return func(config, exec, context); + return func(config, context, exec); } return nullptr; } diff --git a/core/solver/cg.cpp b/core/solver/cg.cpp index 7d1821e19e0..f0a1e5ffb15 100644 --- a/core/solver/cg.cpp +++ b/core/solver/cg.cpp @@ -28,27 +28,27 @@ namespace config { template <> std::unique_ptr build_from_config(gko::config::LinOpFactoryType::Cg)>( - const gko::config::Config& config, std::shared_ptr& exec, - const gko::config::registry& context) + const gko::config::Config& config, const gko::config::registry& context, + std::shared_ptr& exec) { // TODO: select the type, always using double as demo. // extract the following to another function (or build_from_config) to make // this function only select type - auto factory = gko::solver::Cg::build_from_config(config, context); - // handle parameter requires exec - // criteria and preconditioner are almost in each solver -> to another - // function. - factory.with_criteria( - gko::stop::Iteration::build().with_max_iters(1u).on(exec)); + std::string val_str = "double"; // get from default pack { - auto str = config.find("preconditioner"); - if (str != config.end()) { - // assume we have the config for nest object - factory.with_preconditioner(build_from_config( - Config{{"Type", str->second}}, exec, context)); + auto it = config.find("ValueType"); + if (it != config.end()) { + val_str = it->second; } } - return std::move(factory.on(exec)); + if (val_str == "double") { + return gko::solver::Cg::build_from_config(config, context, + exec); + } else if (val_str == "float") { + return gko::solver::Cg::build_from_config(config, context, exec); + } + + return nullptr; } @@ -68,6 +68,38 @@ GKO_REGISTER_OPERATION(step_2, cg::step_2); } // anonymous namespace } // namespace cg +template +std::unique_ptr::Factory> +Cg::build_from_config(const gko::config::Config& config, + const gko::config::registry& context, + std::shared_ptr exec) +{ + auto factory = Factory::create(); + { + auto str = config.find("generated_preconditioner"); + if (str != config.end()) { + auto linop = context.search_data(str->second); + factory.with_generated_preconditioner(linop); + } + } + // handle parameter requires exec + // criteria and preconditioner are almost in each solver -> to another + // function. + factory.with_criteria( + gko::stop::Iteration::build().with_max_iters(1u).on(exec)); + { + auto str = config.find("preconditioner"); + if (str != config.end()) { + // assume we have the config for nest object + factory.with_preconditioner(gko::config::build_from_config( + gko::config::Config{{"Type", str->second}}, context, exec)); + } + } + // can also handle preconditioner, criterion here if they are in + // context. + return factory.on(exec); +} + template std::unique_ptr Cg::transpose() const diff --git a/core/test/config/config.cpp b/core/test/config/config.cpp index 8fa0df62fd2..b18907e57f8 100644 --- a/core/test/config/config.cpp +++ b/core/test/config/config.cpp @@ -83,8 +83,8 @@ TYPED_TEST(Config, GenerateObject) auto config_map = gko::config::generate_config_map(); auto reg = gko::config::registry(config_map); - auto obj = gko::config::build_from_config<0>(gko::config::Config{}, - this->exec, reg); + auto obj = gko::config::build_from_config<0>(gko::config::Config{}, reg, + this->exec); ASSERT_NE(dynamic_cast::Factory*>(obj.get()), nullptr); @@ -98,8 +98,8 @@ TYPED_TEST(Config, GenerateObjectWithData) reg.emplace("precond", this->mtx); auto obj = gko::config::build_from_config<0>( - gko::config::Config{{"generated_preconditioner", "precond"}}, - this->exec, reg); + gko::config::Config{{"generated_preconditioner", "precond"}}, reg, + this->exec); ASSERT_NE(dynamic_cast::Factory*>(obj.get()), nullptr); @@ -116,7 +116,7 @@ TYPED_TEST(Config, GenerateObjectWithPreconditioner) auto reg = gko::config::registry(config_map); auto obj = gko::config::build_from_config<0>( - gko::config::Config{{"preconditioner", "Cg"}}, this->exec, reg); + gko::config::Config{{"preconditioner", "Cg"}}, reg, this->exec); ASSERT_NE(dynamic_cast::Factory*>(obj.get()), nullptr); @@ -132,8 +132,8 @@ TYPED_TEST(Config, GenerateObjectWithCustomBuild) auto config_map = gko::config::generate_config_map(); config_map["Custom"] = [](const gko::config::Config& config, - std::shared_ptr& exec, - const gko::config::registry& context) { + const gko::config::registry& context, + std::shared_ptr& exec) { return gko::solver::Bicg::build() .with_criteria( gko::stop::Iteration::build().with_max_iters(2u).on(exec)) @@ -142,7 +142,7 @@ TYPED_TEST(Config, GenerateObjectWithCustomBuild) auto reg = gko::config::registry(config_map); auto obj = gko::config::build_from_config<0>( - gko::config::Config{{"preconditioner", "Custom"}}, this->exec, reg); + gko::config::Config{{"preconditioner", "Custom"}}, reg, this->exec); ASSERT_NE(dynamic_cast::Factory*>(obj.get()), nullptr); diff --git a/include/ginkgo/core/config/config.hpp b/include/ginkgo/core/config/config.hpp index d8a07a5e914..3df7d43b1a2 100644 --- a/include/ginkgo/core/config/config.hpp +++ b/include/ginkgo/core/config/config.hpp @@ -55,13 +55,13 @@ enum LinOpFactoryType : int { Cg = 0 }; // IT selection, we do not need it in public. template std::unique_ptr build_from_config( - const gko::config::Config& config, std::shared_ptr& exec, - const gko::config::registry& context); + const gko::config::Config& config, const gko::config::registry& context, + std::shared_ptr& exec); // The main function std::unique_ptr build_from_config( - const gko::config::Config& config, std::shared_ptr& exec, - const gko::config::registry& context); + const gko::config::Config& config, const gko::config::registry& context, + std::shared_ptr& exec); BuildFromConfigMap generate_config_map(); diff --git a/include/ginkgo/core/config/registry.hpp b/include/ginkgo/core/config/registry.hpp index 9db9122f24e..160a903a200 100644 --- a/include/ginkgo/core/config/registry.hpp +++ b/include/ginkgo/core/config/registry.hpp @@ -53,7 +53,7 @@ using LinOpFactoryMap = using Config = std::map; class registry; using BuildFunctionType = std::function( - const Config&, std::shared_ptr&, const registry&)>; + const Config&, const registry&, std::shared_ptr&)>; using BuildFromConfigMap = std::map; /** diff --git a/include/ginkgo/core/solver/cg.hpp b/include/ginkgo/core/solver/cg.hpp index c337516a9a0..d8c33a905bc 100644 --- a/include/ginkgo/core/solver/cg.hpp +++ b/include/ginkgo/core/solver/cg.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,10 @@ class Cg : public EnableLinOp>, public Transposable { friend class EnableLinOp; friend class EnablePolymorphicObject; + friend std::unique_ptr gko::config::build_from_config< + static_cast(gko::config::LinOpFactoryType::Cg)>( + const gko::config::Config&, const gko::config::registry&, + std::shared_ptr&); public: using value_type = ValueType; @@ -73,24 +78,12 @@ class Cg : public EnableLinOp>, GKO_ENABLE_LIN_OP_FACTORY(Cg, parameters, Factory); GKO_ENABLE_BUILD_METHOD(Factory); - static auto build_from_config(const gko::config::Config& config, - const gko::config::registry& context) - -> decltype(Factory::create()) - { - auto factory = Factory::create(); - { - auto str = config.find("generated_preconditioner"); - if (str != config.end()) { - auto linop = context.search_data(str->second); - factory.with_generated_preconditioner(linop); - } - } - // can also handle preconditioner, criterion here if they are in - // context. - return factory; - } protected: + static std::unique_ptr build_from_config( + const gko::config::Config& config, const gko::config::registry& context, + std::shared_ptr exec); + void apply_impl(const LinOp* b, LinOp* x) const override; template From 284afede4c5ab4a7bb172db96c259082687fac13 Mon Sep 17 00:00:00 2001 From: "Yuhsiang M. Tsai" Date: Fri, 11 Aug 2023 12:24:58 +0200 Subject: [PATCH 03/25] add type descripitor --- core/config/config.cpp | 4 +-- core/solver/cg.cpp | 26 +++++++++++----- core/test/config/config.cpp | 40 +++++++++++++++++++------ include/ginkgo/core/config/config.hpp | 6 ++-- include/ginkgo/core/config/registry.hpp | 23 +++++++++++++- include/ginkgo/core/solver/cg.hpp | 6 ++-- 6 files changed, 81 insertions(+), 24 deletions(-) diff --git a/core/config/config.cpp b/core/config/config.cpp index 412ee2240ca..419ba2abbcf 100644 --- a/core/config/config.cpp +++ b/core/config/config.cpp @@ -48,12 +48,12 @@ BuildFromConfigMap generate_config_map() std::unique_ptr build_from_config( const gko::config::Config& config, const gko::config::registry& context, - std::shared_ptr& exec) + std::shared_ptr& exec, TypeDescriptor td) { auto type = config.find("Type"); if (type != config.end()) { auto func = context.get_build_map().at(type->second); - return func(config, context, exec); + return func(config, context, exec, td); } return nullptr; } diff --git a/core/solver/cg.cpp b/core/solver/cg.cpp index f0a1e5ffb15..68886f16605 100644 --- a/core/solver/cg.cpp +++ b/core/solver/cg.cpp @@ -29,23 +29,33 @@ template <> std::unique_ptr build_from_config(gko::config::LinOpFactoryType::Cg)>( const gko::config::Config& config, const gko::config::registry& context, - std::shared_ptr& exec) + std::shared_ptr& exec, gko::config::TypeDescriptor td) { // TODO: select the type, always using double as demo. // extract the following to another function (or build_from_config) to make // this function only select type - std::string val_str = "double"; // get from default pack + std::string val_str = td.first; // get from default pack { auto it = config.find("ValueType"); if (it != config.end()) { val_str = it->second; } + // propagate the type + td.first = val_str; } + // the following can be handled by auto selection. maybe reuse the macro? if (val_str == "double") { - return gko::solver::Cg::build_from_config(config, context, - exec); + return gko::solver::Cg::build_from_config(config, context, exec, + td); } else if (val_str == "float") { - return gko::solver::Cg::build_from_config(config, context, exec); + return gko::solver::Cg::build_from_config(config, context, exec, + td); + } else if (val_str == "complex") { + return gko::solver::Cg>::build_from_config( + config, context, exec, td); + } else if (val_str == "complex") { + return gko::solver::Cg>::build_from_config( + config, context, exec, td); } return nullptr; @@ -72,7 +82,8 @@ template std::unique_ptr::Factory> Cg::build_from_config(const gko::config::Config& config, const gko::config::registry& context, - std::shared_ptr exec) + std::shared_ptr exec, + gko::config::TypeDescriptor td_for_child) { auto factory = Factory::create(); { @@ -92,7 +103,8 @@ Cg::build_from_config(const gko::config::Config& config, if (str != config.end()) { // assume we have the config for nest object factory.with_preconditioner(gko::config::build_from_config( - gko::config::Config{{"Type", str->second}}, context, exec)); + gko::config::Config{{"Type", str->second}}, context, exec, + td_for_child)); } } // can also handle preconditioner, criterion here if they are in diff --git a/core/test/config/config.cpp b/core/test/config/config.cpp index b18907e57f8..351511312f7 100644 --- a/core/test/config/config.cpp +++ b/core/test/config/config.cpp @@ -78,13 +78,13 @@ TYPED_TEST(Config, GenerateMap) } -TYPED_TEST(Config, GenerateObject) +TYPED_TEST(Config, GenerateObjectWithoutDefault) { auto config_map = gko::config::generate_config_map(); auto reg = gko::config::registry(config_map); - auto obj = gko::config::build_from_config<0>(gko::config::Config{}, reg, - this->exec); + auto obj = gko::config::build_from_config<0>( + gko::config::Config{{"ValueType", "double"}}, reg, this->exec); ASSERT_NE(dynamic_cast::Factory*>(obj.get()), nullptr); @@ -99,16 +99,35 @@ TYPED_TEST(Config, GenerateObjectWithData) auto obj = gko::config::build_from_config<0>( gko::config::Config{{"generated_preconditioner", "precond"}}, reg, - this->exec); + this->exec, {"float", ""}); - ASSERT_NE(dynamic_cast::Factory*>(obj.get()), + ASSERT_NE(dynamic_cast::Factory*>(obj.get()), nullptr); - ASSERT_NE(dynamic_cast::Factory*>(obj.get()) + ASSERT_NE(dynamic_cast::Factory*>(obj.get()) ->get_parameters() .generated_preconditioner, nullptr); } +// it is under protected namespace +// TYPED_TEST(Config, GenerateObjectWithDataFromSolver) +// { +// auto config_map = gko::config::generate_config_map(); +// auto reg = gko::config::registry(config_map); +// reg.emplace("precond", this->mtx); + +// auto obj = gko::solver::Cg::build_from_config( +// gko::config::Config{{"generated_preconditioner", "precond"}}, reg, +// this->exec); + +// ASSERT_NE(dynamic_cast::Factory*>(obj.get()), +// nullptr); +// ASSERT_NE(dynamic_cast::Factory*>(obj.get()) +// ->get_parameters() +// .generated_preconditioner, +// nullptr); +// } + TYPED_TEST(Config, GenerateObjectWithPreconditioner) { @@ -116,7 +135,8 @@ TYPED_TEST(Config, GenerateObjectWithPreconditioner) auto reg = gko::config::registry(config_map); auto obj = gko::config::build_from_config<0>( - gko::config::Config{{"preconditioner", "Cg"}}, reg, this->exec); + gko::config::Config{{"ValueType", "double"}, {"preconditioner", "Cg"}}, + reg, this->exec); ASSERT_NE(dynamic_cast::Factory*>(obj.get()), nullptr); @@ -133,7 +153,8 @@ TYPED_TEST(Config, GenerateObjectWithCustomBuild) config_map["Custom"] = [](const gko::config::Config& config, const gko::config::registry& context, - std::shared_ptr& exec) { + std::shared_ptr& exec, + gko::config::TypeDescriptor td_for_child) { return gko::solver::Bicg::build() .with_criteria( gko::stop::Iteration::build().with_max_iters(2u).on(exec)) @@ -142,7 +163,8 @@ TYPED_TEST(Config, GenerateObjectWithCustomBuild) auto reg = gko::config::registry(config_map); auto obj = gko::config::build_from_config<0>( - gko::config::Config{{"preconditioner", "Custom"}}, reg, this->exec); + gko::config::Config{{"preconditioner", "Custom"}}, reg, this->exec, + {"double", ""}); ASSERT_NE(dynamic_cast::Factory*>(obj.get()), nullptr); diff --git a/include/ginkgo/core/config/config.hpp b/include/ginkgo/core/config/config.hpp index 3df7d43b1a2..eedb707e083 100644 --- a/include/ginkgo/core/config/config.hpp +++ b/include/ginkgo/core/config/config.hpp @@ -52,16 +52,16 @@ enum LinOpFactoryType : int { Cg = 0 }; // It is only an intermediate step. If we do not provide the SolverType with VT, -// IT selection, we do not need it in public. +// IT selection, it can be in detail namespace. template std::unique_ptr build_from_config( const gko::config::Config& config, const gko::config::registry& context, - std::shared_ptr& exec); + std::shared_ptr& exec, TypeDescriptor td = {"", ""}); // The main function std::unique_ptr build_from_config( const gko::config::Config& config, const gko::config::registry& context, - std::shared_ptr& exec); + std::shared_ptr& exec, TypeDescriptor td = {"", ""}); BuildFromConfigMap generate_config_map(); diff --git a/include/ginkgo/core/config/registry.hpp b/include/ginkgo/core/config/registry.hpp index 160a903a200..e6832d4b394 100644 --- a/include/ginkgo/core/config/registry.hpp +++ b/include/ginkgo/core/config/registry.hpp @@ -34,9 +34,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define GKO_PUBLIC_CORE_CONFIG_REGISTRY_HPP_ +#include #include #include #include +#include #include @@ -51,11 +53,14 @@ using LinOpMap = std::unordered_map>; using LinOpFactoryMap = std::unordered_map>; using Config = std::map; +using TypeDescriptor = std::pair; class registry; using BuildFunctionType = std::function( - const Config&, const registry&, std::shared_ptr&)>; + const Config&, const registry&, std::shared_ptr&, + TypeDescriptor)>; using BuildFromConfigMap = std::map; + /** * map_type gives the map type according to the base type of given type. * @@ -184,6 +189,22 @@ inline const LinOpFactoryMap& registry::get_map_impl() const } +template +struct type_string { + static std::string str() { return "N"; }; +}; + +#define TYPE_STRING_OVERLOAD(_type, _str) \ + template <> \ + struct type_string<_type> { \ + static std::string str() { return _str; } \ + } + +TYPE_STRING_OVERLOAD(double, "double"); +TYPE_STRING_OVERLOAD(float, "float"); +TYPE_STRING_OVERLOAD(std::complex, "complex"); +TYPE_STRING_OVERLOAD(std::complex, "complex"); + } // namespace config } // namespace gko diff --git a/include/ginkgo/core/solver/cg.hpp b/include/ginkgo/core/solver/cg.hpp index d8c33a905bc..bebc087c359 100644 --- a/include/ginkgo/core/solver/cg.hpp +++ b/include/ginkgo/core/solver/cg.hpp @@ -53,7 +53,7 @@ class Cg : public EnableLinOp>, friend std::unique_ptr gko::config::build_from_config< static_cast(gko::config::LinOpFactoryType::Cg)>( const gko::config::Config&, const gko::config::registry&, - std::shared_ptr&); + std::shared_ptr&, gko::config::TypeDescriptor); public: using value_type = ValueType; @@ -82,7 +82,9 @@ class Cg : public EnableLinOp>, protected: static std::unique_ptr build_from_config( const gko::config::Config& config, const gko::config::registry& context, - std::shared_ptr exec); + std::shared_ptr exec, + gko::config::TypeDescriptor td_for_child = { + gko::config::type_string::str(), ""}); void apply_impl(const LinOp* b, LinOp* x) const override; From 9d11041b8ca4020152aec84dc0750f40024ef68e Mon Sep 17 00:00:00 2001 From: "Yuhsiang M. Tsai" Date: Fri, 11 Aug 2023 22:50:50 +0200 Subject: [PATCH 04/25] add the general type dispatch --- core/config/config.hpp | 139 ++++++++++++++++++++++++ core/config/dispatch.hpp | 115 ++++++++++++++++++++ core/solver/cg.cpp | 43 ++------ include/ginkgo/core/config/config.hpp | 10 ++ include/ginkgo/core/config/registry.hpp | 24 ++++ include/ginkgo/core/solver/cg.hpp | 16 ++- 6 files changed, 308 insertions(+), 39 deletions(-) create mode 100644 core/config/config.hpp create mode 100644 core/config/dispatch.hpp diff --git a/core/config/config.hpp b/core/config/config.hpp new file mode 100644 index 00000000000..6f49e7ca88a --- /dev/null +++ b/core/config/config.hpp @@ -0,0 +1,139 @@ +/************************************************************* +Copyright (c) 2017-2023, the Ginkgo authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************/ + +#ifndef GKO_CORE_CONFIG_CONFIG_HPP_ +#define GKO_CORE_CONFIG_CONFIG_HPP_ + + +#include + + +#include +#include +#include + +namespace gko { +namespace config { + +/** + * This function is to update the default type setting from current config. + * + * @note It might update the unused type for the current class. + */ +inline TypeDescriptor update_type(const Config& config, + const TypeDescriptor& td) +{ + TypeDescriptor updated = td; + auto it = config.find("ValueType"); + if (it != config.end()) { + updated.first = it->second; + } + it = config.find("IndexType"); + if (it != config.end()) { + updated.second = it->second; + } + return updated; +} + + +using item = std::string; + +template +inline std::shared_ptr get_pointer(const item& item, const registry& context, + std::shared_ptr exec, + TypeDescriptor td) +{ + std::shared_ptr ptr; + using T_non_const = std::remove_const_t; + ptr = context.search_data(item); + assert(ptr.get() != nullptr); + return std::move(ptr); +} + +template <> +inline std::shared_ptr get_pointer( + const item& item, const registry& context, + std::shared_ptr exec, TypeDescriptor td) +{ + std::shared_ptr ptr; + ptr = context.search_data(item); + // handle object is item + assert(ptr.get() != nullptr); + return std::move(ptr); +} + + +template <> +inline std::shared_ptr +get_pointer(const item& item, + const registry& context, + std::shared_ptr exec, + TypeDescriptor td) +{ + std::shared_ptr ptr; + ptr = context.search_data(item); + // handle object is item + assert(ptr.get() != nullptr); + return std::move(ptr); +} + + +template +inline std::vector> get_pointer_vector( + const item& item, const registry& context, + std::shared_ptr exec, TypeDescriptor td) +{ + std::vector> res; + // for loop in item + res.push_back(get_pointer(item, context, exec, td)); + return res; +} + +#define SET_POINTER(_factory, _param_type, _param_name, _config, _context, \ + _exec, _td) \ + { \ + auto it = _config.find(#_param_name); \ + if (it != _config.end()) { \ + _factory.with_##_param_name(gko::config::get_pointer<_param_type>( \ + it->second, _context, _exec, _td)); \ + } \ + } \ + static_assert(true, \ + "This assert is used to counter the false positive extra " \ + "semi-colon warnings") + + +} // namespace config +} // namespace gko + + +#endif // GKO_CORE_CONFIG_CONFIG_HPP_ diff --git a/core/config/dispatch.hpp b/core/config/dispatch.hpp new file mode 100644 index 00000000000..0ba85a20c0d --- /dev/null +++ b/core/config/dispatch.hpp @@ -0,0 +1,115 @@ +/************************************************************* +Copyright (c) 2017-2023, the Ginkgo authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************/ + +#ifndef GKO_CORE_CONFIG_DISPATCH_HPP_ +#define GKO_CORE_CONFIG_DISPATCH_HPP_ + + +#include +#include + + +#include +#include +#include + + +namespace gko { +namespace config { + + +/** + * This function is to dispatch the type from runtime parameter. + * The concrete class need to have static member function + * build_from_config(Config, registry, std::shared_ptr, + * TypeDescriptor) + */ +template