From f20ffc8f104cec8b96795e8cdf02d920f4b5fb08 Mon Sep 17 00:00:00 2001 From: guilpier-code <62292552+guilpier-code@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:01:29 +0200 Subject: [PATCH] API modeler - 1.1.b (#2391) The goal of this PR is to introduce and unit test class **LinearProblemBuilder**. This class is an orchestration class, and therefore makes other classes collaborate. What was done : - Remove **LinearProblemBuilder::solve()** : this method was planned in the specifications, but it extends the builder responsibility scope. Once modified by the builder, the problem can be solved by calling its own **solve()** method. - Add methods **numVariables()** and **numConstraints()** to base class **ILinearProblem** for unit tests reason. - **CMakeLists.txt** file for modeler API was split into 2 **\*.cmake** files - **Trial for a dependency simplification** : In the original / specified architecture, **LinearProblemBuilder** collaborates with classes : - **LinearProblemData** - **ILinearProblem** (interface for LP) - **LinearProblemFiller** : this class also depends on previous classes Here, we allowed ourselves to reduce dependencies of this builder class (is this simplification relevant ?) : class **LinearProblemBuilder** currently only depends on **LinearProblemFiller**, while fillers keeps its planned dependencies on **LinearProblemData** and **ILinearProblem**. Indeed, any filler is in charge to modify problem (using LP data), the but the builder only manipulates fillers. - **Mocking fillers** : fillers are the entities responsible for creating variables and constraints in the linear problem. To do that, they need the LP itself as well as the LP data (see fillers constructor). We created an abstract base filler. All concrete filler inherit from it. Class **LinearProblemBuilder** is constructed from a vector of fillers. - Several unit tests were created for class **LinearProblemBuilder** --------- Co-authored-by: Abdoulbari Zaher <32519851+a-zakir@users.noreply.github.com> --- src/solver/modeler/api/CMakeLists.txt | 3 +- .../solver/modeler/api/linearProblem.h | 2 + .../solver/modeler/api/linearProblemBuilder.h | 9 +- .../solver/modeler/api/linearProblemFiller.h | 9 +- .../modeler/api/linearProblemBuilder.cpp | 21 ++++ src/solver/modeler/ortoolsImpl/CMakeLists.txt | 2 +- .../modeler/ortoolsImpl/linearProblem.h | 3 +- .../modeler/ortoolsImpl/linearProblem.cpp | 10 ++ .../src/solver/modeler/api/CMakeLists.txt | 16 ++- .../api/mock-fillers/OneConstraintFiller.h | 30 +++++ .../modeler/api/mock-fillers/OneVarFiller.h | 35 ++++++ .../TwoVarsTwoConstraintsFiller.h | 33 +++++ .../modeler/api/testModelerLPbuilder.cpp | 115 ++++++++++++++++++ ...> testModelerLinearProblemWithOrtools.cpp} | 1 - .../src/solver/modeler/api/test_main.cpp | 26 ++++ 15 files changed, 298 insertions(+), 17 deletions(-) create mode 100644 src/solver/modeler/api/linearProblemBuilder.cpp create mode 100644 src/tests/src/solver/modeler/api/mock-fillers/OneConstraintFiller.h create mode 100644 src/tests/src/solver/modeler/api/mock-fillers/OneVarFiller.h create mode 100644 src/tests/src/solver/modeler/api/mock-fillers/TwoVarsTwoConstraintsFiller.h create mode 100644 src/tests/src/solver/modeler/api/testModelerLPbuilder.cpp rename src/tests/src/solver/modeler/api/{testApiOrtoolsLinearProblem.cpp => testModelerLinearProblemWithOrtools.cpp} (99%) create mode 100644 src/tests/src/solver/modeler/api/test_main.cpp diff --git a/src/solver/modeler/api/CMakeLists.txt b/src/solver/modeler/api/CMakeLists.txt index 049c2dfa8e..41789ae9cb 100644 --- a/src/solver/modeler/api/CMakeLists.txt +++ b/src/solver/modeler/api/CMakeLists.txt @@ -1,4 +1,4 @@ -set(PROJ optim_api) +set(PROJ modeler_api) set(SRC_API include/antares/solver/modeler/api/mipVariable.h @@ -14,6 +14,7 @@ set(SRC_API include/antares/solver/modeler/api/linearProblemBuilder.h linearProblemData.cpp + linearProblemBuilder.cpp ) add_library(${PROJ} ${SRC_API}) diff --git a/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblem.h b/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblem.h index b997242ec7..38006291e0 100644 --- a/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblem.h +++ b/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblem.h @@ -47,10 +47,12 @@ class ILinearProblem /// Create a integer variable virtual IMipVariable* addIntVariable(double lb, double ub, const std::string& name) = 0; virtual IMipVariable* getVariable(const std::string& name) const = 0; + virtual int variableCount() const = 0; /// Add a bounded constraint to the problem virtual IMipConstraint* addConstraint(double lb, double ub, const std::string& name) = 0; virtual IMipConstraint* getConstraint(const std::string& name) const = 0; + virtual int constraintCount() const = 0; /// Set the objective coefficient for a given variable virtual void setObjectiveCoefficient(IMipVariable* var, double coefficient) = 0; diff --git a/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblemBuilder.h b/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblemBuilder.h index b4131d4f6b..c5b4419113 100644 --- a/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblemBuilder.h +++ b/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblemBuilder.h @@ -24,7 +24,6 @@ #include #include "linearProblemFiller.h" -#include "mipSolution.h" namespace Antares::Solver::Modeler::Api { @@ -32,9 +31,11 @@ namespace Antares::Solver::Modeler::Api class LinearProblemBuilder { public: - virtual void LinearProblemBuilder(std::vector fillers) = 0; - virtual void build(LinearProblemData* data) = 0; - virtual MipSolution* solve() = 0; + explicit LinearProblemBuilder(const std::vector& fillers); + void build(ILinearProblem& pb, LinearProblemData& data); + +private: + const std::vector& fillers_; }; } // namespace Antares::Solver::Modeler::Api diff --git a/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblemFiller.h b/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblemFiller.h index b337dea247..aad1fa3ce6 100644 --- a/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblemFiller.h +++ b/src/solver/modeler/api/include/antares/solver/modeler/api/linearProblemFiller.h @@ -21,6 +21,8 @@ #pragma once +#include + #include #include @@ -30,9 +32,10 @@ namespace Antares::Solver::Modeler::Api class LinearProblemFiller { public: - virtual void addVariables(LinearProblem* problem, LinearProblemData* data) = 0; - virtual void addConstraints(LinearProblem* problem, LinearProblemData* data) = 0; - virtual void addObjectiveCoefficients(LinearProblem* problem, LinearProblemData* data) = 0; + virtual void addVariables(ILinearProblem& pb, LinearProblemData& data) = 0; + virtual void addConstraints(ILinearProblem& pb, LinearProblemData& data) = 0; + virtual void addObjective(ILinearProblem& pb, LinearProblemData& data) = 0; + virtual ~LinearProblemFiller() = default; }; } // namespace Antares::Solver::Modeler::Api diff --git a/src/solver/modeler/api/linearProblemBuilder.cpp b/src/solver/modeler/api/linearProblemBuilder.cpp new file mode 100644 index 0000000000..f9d6103152 --- /dev/null +++ b/src/solver/modeler/api/linearProblemBuilder.cpp @@ -0,0 +1,21 @@ +#include +#include + +#include + +namespace Antares::Solver::Modeler::Api +{ + +LinearProblemBuilder::LinearProblemBuilder(const std::vector& fillers): + fillers_(fillers) +{ +} + +void LinearProblemBuilder::build(ILinearProblem& pb, LinearProblemData& data) +{ + std::ranges::for_each(fillers_, [&](const auto& filler) { filler->addVariables(pb, data); }); + std::ranges::for_each(fillers_, [&](const auto& filler) { filler->addConstraints(pb, data); }); + std::ranges::for_each(fillers_, [&](const auto& filler) { filler->addObjective(pb, data); }); +} + +} // namespace Antares::Solver::Modeler::Api diff --git a/src/solver/modeler/ortoolsImpl/CMakeLists.txt b/src/solver/modeler/ortoolsImpl/CMakeLists.txt index ef94b552cc..309fb67fa8 100644 --- a/src/solver/modeler/ortoolsImpl/CMakeLists.txt +++ b/src/solver/modeler/ortoolsImpl/CMakeLists.txt @@ -23,7 +23,7 @@ add_library(Antares::${PROJ} ALIAS ${PROJ}) target_link_libraries(${PROJ} PUBLIC - Antares::optim_api + Antares::modeler_api Antares::logs Antares::solverUtils ortools::ortools diff --git a/src/solver/modeler/ortoolsImpl/include/antares/solver/modeler/ortoolsImpl/linearProblem.h b/src/solver/modeler/ortoolsImpl/include/antares/solver/modeler/ortoolsImpl/linearProblem.h index 6a34ccfcc5..d4e6e52c02 100644 --- a/src/solver/modeler/ortoolsImpl/include/antares/solver/modeler/ortoolsImpl/linearProblem.h +++ b/src/solver/modeler/ortoolsImpl/include/antares/solver/modeler/ortoolsImpl/linearProblem.h @@ -44,11 +44,12 @@ class OrtoolsLinearProblem final: public Api::ILinearProblem OrtoolsMipVariable* addNumVariable(double lb, double ub, const std::string& name) override; OrtoolsMipVariable* addIntVariable(double lb, double ub, const std::string& name) override; - OrtoolsMipVariable* getVariable(const std::string& name) const override; + int variableCount() const override; OrtoolsMipConstraint* addConstraint(double lb, double ub, const std::string& name) override; OrtoolsMipConstraint* getConstraint(const std::string& name) const override; + int constraintCount() const override; void setObjectiveCoefficient(Api::IMipVariable* var, double coefficient) override; double getObjectiveCoefficient(const Api::IMipVariable* var) const override; diff --git a/src/solver/modeler/ortoolsImpl/linearProblem.cpp b/src/solver/modeler/ortoolsImpl/linearProblem.cpp index 7c6593fa52..1d8bf981bc 100644 --- a/src/solver/modeler/ortoolsImpl/linearProblem.cpp +++ b/src/solver/modeler/ortoolsImpl/linearProblem.cpp @@ -93,6 +93,11 @@ OrtoolsMipVariable* OrtoolsLinearProblem::getVariable(const std::string& name) c return variables_.at(name).get(); } +int OrtoolsLinearProblem::variableCount() const +{ + return mpSolver_->NumVariables(); +} + OrtoolsMipConstraint* OrtoolsLinearProblem::addConstraint(double lb, double ub, const std::string& name) @@ -120,6 +125,11 @@ OrtoolsMipConstraint* OrtoolsLinearProblem::getConstraint(const std::string& nam return constraints_.at(name).get(); } +int OrtoolsLinearProblem::constraintCount() const +{ + return mpSolver_->NumConstraints(); +} + static const operations_research::MPVariable* getMpVar(const Api::IMipVariable* var) { diff --git a/src/tests/src/solver/modeler/api/CMakeLists.txt b/src/tests/src/solver/modeler/api/CMakeLists.txt index 0299f3ec0b..ae048140aa 100644 --- a/src/tests/src/solver/modeler/api/CMakeLists.txt +++ b/src/tests/src/solver/modeler/api/CMakeLists.txt @@ -1,6 +1,12 @@ -set(EXECUTABLE_NAME tests-modeler-api-ortools) -add_executable(${EXECUTABLE_NAME} testApiOrtoolsLinearProblem.cpp) +set(EXECUTABLE_NAME unit-tests-for-modeler-api) +add_executable(${EXECUTABLE_NAME}) +target_sources(${EXECUTABLE_NAME} + PRIVATE + test_main.cpp + testModelerLinearProblemWithOrtools.cpp + testModelerLPbuilder.cpp +) target_include_directories(${EXECUTABLE_NAME} PRIVATE "${src_solver_optimisation}" @@ -12,9 +18,7 @@ target_link_libraries(${EXECUTABLE_NAME} Antares::modeler-ortools-impl ) -# Storing tests-ts-numbers under the folder Unit-tests in the IDE set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) +add_test(NAME ${EXECUTABLE_NAME} COMMAND ${EXECUTABLE_NAME}) +set_property(TEST ${EXECUTABLE_NAME} PROPERTY LABELS unit) -add_test(NAME test-modeler-api-ortools COMMAND ${EXECUTABLE_NAME}) - -set_property(TEST test-modeler-api-ortools PROPERTY LABELS unit) diff --git a/src/tests/src/solver/modeler/api/mock-fillers/OneConstraintFiller.h b/src/tests/src/solver/modeler/api/mock-fillers/OneConstraintFiller.h new file mode 100644 index 0000000000..cb1f9db3e9 --- /dev/null +++ b/src/tests/src/solver/modeler/api/mock-fillers/OneConstraintFiller.h @@ -0,0 +1,30 @@ +#pragma once + +#include "antares/solver/modeler/api/linearProblemFiller.h" + +namespace Antares::Solver::Modeler::Api +{ + +class OneConstraintFiller: public LinearProblemFiller +{ +public: + explicit OneConstraintFiller() = default; + void addVariables(ILinearProblem& pb, LinearProblemData& data) override; + void addConstraints(ILinearProblem& pb, LinearProblemData& data) override; + void addObjective(ILinearProblem& pb, LinearProblemData& data) override; +}; + +void OneConstraintFiller::addVariables(ILinearProblem& pb, LinearProblemData& data) +{ +} + +void OneConstraintFiller::addConstraints(ILinearProblem& pb, LinearProblemData& data) +{ + pb.addConstraint(1, 2, "constraint-by-OneConstraintFiller"); +} + +void OneConstraintFiller::addObjective(ILinearProblem& pb, LinearProblemData& data) +{ +} + +} // namespace Antares::Solver::Modeler::Api diff --git a/src/tests/src/solver/modeler/api/mock-fillers/OneVarFiller.h b/src/tests/src/solver/modeler/api/mock-fillers/OneVarFiller.h new file mode 100644 index 0000000000..5d2ba08b55 --- /dev/null +++ b/src/tests/src/solver/modeler/api/mock-fillers/OneVarFiller.h @@ -0,0 +1,35 @@ +#pragma once + +#include "antares/solver/modeler/api/linearProblemFiller.h" + +namespace Antares::Solver::Modeler::Api +{ + +class OneVarFiller: public LinearProblemFiller +{ +public: + explicit OneVarFiller() = default; + void addVariables(ILinearProblem& pb, LinearProblemData& data) override; + void addConstraints(ILinearProblem& pb, LinearProblemData& data) override; + void addObjective(ILinearProblem& pb, LinearProblemData& data) override; + +private: + std::string added_var_name_ = "var-by-OneVarFiller"; +}; + +void OneVarFiller::addVariables(ILinearProblem& pb, LinearProblemData& data) +{ + pb.addNumVariable(0, 1, added_var_name_); +} + +void OneVarFiller::addConstraints(ILinearProblem& pb, LinearProblemData& data) +{ +} + +void OneVarFiller::addObjective(ILinearProblem& pb, LinearProblemData& data) +{ + auto* var = pb.getVariable(added_var_name_); + pb.setObjectiveCoefficient(var, 1); +} + +} // namespace Antares::Solver::Modeler::Api diff --git a/src/tests/src/solver/modeler/api/mock-fillers/TwoVarsTwoConstraintsFiller.h b/src/tests/src/solver/modeler/api/mock-fillers/TwoVarsTwoConstraintsFiller.h new file mode 100644 index 0000000000..e8671644df --- /dev/null +++ b/src/tests/src/solver/modeler/api/mock-fillers/TwoVarsTwoConstraintsFiller.h @@ -0,0 +1,33 @@ +#pragma once + +#include "antares/solver/modeler/api/linearProblemFiller.h" + +namespace Antares::Solver::Modeler::Api +{ + +class TwoVarsTwoConstraintsFiller: public LinearProblemFiller +{ +public: + explicit TwoVarsTwoConstraintsFiller() = default; + void addVariables(ILinearProblem& pb, LinearProblemData& data) override; + void addConstraints(ILinearProblem& pb, LinearProblemData& data) override; + void addObjective(ILinearProblem& pb, LinearProblemData& data) override; +}; + +void TwoVarsTwoConstraintsFiller::addVariables(ILinearProblem& pb, LinearProblemData& data) +{ + pb.addNumVariable(0, 1, "var-1-by-TwoVarsTwoConstraintsFiller"); + pb.addNumVariable(0, 3, "var-2-by-TwoVarsTwoConstraintsFiller"); +} + +void TwoVarsTwoConstraintsFiller::addConstraints(ILinearProblem& pb, LinearProblemData& data) +{ + pb.addConstraint(1, 2, "constr-1-by-TwoVarsTwoConstraintsFiller"); + pb.addConstraint(1, 3, "constr-2-by-TwoVarsTwoConstraintsFiller"); +} + +void TwoVarsTwoConstraintsFiller::addObjective(ILinearProblem& pb, LinearProblemData& data) +{ +} + +} // namespace Antares::Solver::Modeler::Api diff --git a/src/tests/src/solver/modeler/api/testModelerLPbuilder.cpp b/src/tests/src/solver/modeler/api/testModelerLPbuilder.cpp new file mode 100644 index 0000000000..ccdccf251a --- /dev/null +++ b/src/tests/src/solver/modeler/api/testModelerLPbuilder.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Antares_Simulator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ +#define WIN32_LEAN_AND_MEAN + +#include + +#include +#include + +#include "mock-fillers/OneConstraintFiller.h" +#include "mock-fillers/OneVarFiller.h" +#include "mock-fillers/TwoVarsTwoConstraintsFiller.h" + +using namespace Antares::Solver::Modeler::Api; +using namespace Antares::Solver::Modeler::OrtoolsImpl; + +struct Fixture +{ + Fixture() + { + pb = std::make_unique(false, "sirius"); + } + + std::vector fillers; + LinearProblemData LP_Data; + std::unique_ptr pb; +}; + +BOOST_AUTO_TEST_SUITE(tests_on_linear_problem_builder) + +BOOST_FIXTURE_TEST_CASE(no_filler_given_to_builder___nothing_built, Fixture) +{ + LinearProblemBuilder lpBuilder(fillers); + lpBuilder.build(*pb, LP_Data); + + BOOST_CHECK_EQUAL(pb->variableCount(), 0); + BOOST_CHECK_EQUAL(pb->constraintCount(), 0); +} + +BOOST_FIXTURE_TEST_CASE(one_var_filler___the_var_is_built, Fixture) +{ + auto oneVarFiller = std::make_unique(); + fillers = {oneVarFiller.get()}; + + LinearProblemBuilder lpBuilder(fillers); + lpBuilder.build(*pb, LP_Data); + + BOOST_CHECK_EQUAL(pb->variableCount(), 1); + BOOST_CHECK_EQUAL(pb->constraintCount(), 0); + auto* var = pb->getVariable("var-by-OneVarFiller"); + BOOST_CHECK(var); + BOOST_CHECK_EQUAL(pb->getObjectiveCoefficient(var), 1); +} + +BOOST_FIXTURE_TEST_CASE(one_constraint_filler___the_constraint_is_built, Fixture) +{ + auto oneConstrFiller = std::make_unique(); + fillers = {oneConstrFiller.get()}; + + LinearProblemBuilder lpBuilder(fillers); + lpBuilder.build(*pb, LP_Data); + + BOOST_CHECK_EQUAL(pb->variableCount(), 0); + BOOST_CHECK_EQUAL(pb->constraintCount(), 1); + BOOST_CHECK(pb->getConstraint("constraint-by-OneConstraintFiller")); +} + +BOOST_FIXTURE_TEST_CASE(two_fillers_given_to_builder___all_is_built, Fixture) +{ + auto oneVarFiller = std::make_unique(); + auto oneConstrFiller = std::make_unique(); + + fillers = {oneVarFiller.get(), oneConstrFiller.get()}; + + LinearProblemBuilder lpBuilder(fillers); + lpBuilder.build(*pb, LP_Data); + + BOOST_CHECK_EQUAL(pb->constraintCount(), 1); + BOOST_CHECK(pb->getConstraint("constraint-by-OneConstraintFiller")); + BOOST_CHECK_EQUAL(pb->variableCount(), 1); +} + +BOOST_FIXTURE_TEST_CASE(three_fillers_given_to_builder___3_vars_3_constr_are_built, Fixture) +{ + auto oneVarFiller = std::make_unique(); + auto oneConstrFiller = std::make_unique(); + auto twoVarsTwoConstrFiller = std::make_unique(); + fillers = {oneVarFiller.get(), oneConstrFiller.get(), twoVarsTwoConstrFiller.get()}; + + LinearProblemBuilder lpBuilder(fillers); + lpBuilder.build(*pb, LP_Data); + + BOOST_CHECK_EQUAL(pb->variableCount(), 3); + BOOST_CHECK_EQUAL(pb->constraintCount(), 3); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/src/solver/modeler/api/testApiOrtoolsLinearProblem.cpp b/src/tests/src/solver/modeler/api/testModelerLinearProblemWithOrtools.cpp similarity index 99% rename from src/tests/src/solver/modeler/api/testApiOrtoolsLinearProblem.cpp rename to src/tests/src/solver/modeler/api/testModelerLinearProblemWithOrtools.cpp index 06bbcb6669..7c1f8f84e3 100644 --- a/src/tests/src/solver/modeler/api/testApiOrtoolsLinearProblem.cpp +++ b/src/tests/src/solver/modeler/api/testModelerLinearProblemWithOrtools.cpp @@ -18,7 +18,6 @@ * You should have received a copy of the Mozilla Public Licence 2.0 * along with Antares_Simulator. If not, see . */ -#define BOOST_TEST_MODULE test modeler api ortools #define WIN32_LEAN_AND_MEAN diff --git a/src/tests/src/solver/modeler/api/test_main.cpp b/src/tests/src/solver/modeler/api/test_main.cpp new file mode 100644 index 0000000000..0d5864e717 --- /dev/null +++ b/src/tests/src/solver/modeler/api/test_main.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Antares_Simulator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#define BOOST_TEST_MODULE test modeler api + +#define WIN32_LEAN_AND_MEAN + +#include