Skip to content

Commit

Permalink
API modeler - 1.1.b (#2391)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
guilpier-code and a-zakir authored Sep 27, 2024
1 parent 8ce6107 commit f20ffc8
Show file tree
Hide file tree
Showing 15 changed files with 298 additions and 17 deletions.
3 changes: 2 additions & 1 deletion src/solver/modeler/api/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(PROJ optim_api)
set(PROJ modeler_api)

set(SRC_API
include/antares/solver/modeler/api/mipVariable.h
Expand All @@ -14,6 +14,7 @@ set(SRC_API
include/antares/solver/modeler/api/linearProblemBuilder.h

linearProblemData.cpp
linearProblemBuilder.cpp
)

add_library(${PROJ} ${SRC_API})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@
#include <vector>

#include "linearProblemFiller.h"
#include "mipSolution.h"

namespace Antares::Solver::Modeler::Api
{

class LinearProblemBuilder
{
public:
virtual void LinearProblemBuilder(std::vector<LinearProblemFiller*> fillers) = 0;
virtual void build(LinearProblemData* data) = 0;
virtual MipSolution* solve() = 0;
explicit LinearProblemBuilder(const std::vector<LinearProblemFiller*>& fillers);
void build(ILinearProblem& pb, LinearProblemData& data);

private:
const std::vector<LinearProblemFiller*>& fillers_;
};

} // namespace Antares::Solver::Modeler::Api
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#pragma once

#include <memory>

#include <antares/solver/modeler/api/linearProblem.h>
#include <antares/solver/modeler/api/linearProblemData.h>

Expand All @@ -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
21 changes: 21 additions & 0 deletions src/solver/modeler/api/linearProblemBuilder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <algorithm>
#include <memory>

#include <antares/solver/modeler/api/linearProblemBuilder.h>

namespace Antares::Solver::Modeler::Api
{

LinearProblemBuilder::LinearProblemBuilder(const std::vector<LinearProblemFiller*>& 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
2 changes: 1 addition & 1 deletion src/solver/modeler/ortoolsImpl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions src/solver/modeler/ortoolsImpl/linearProblem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

{
Expand Down
16 changes: 10 additions & 6 deletions src/tests/src/solver/modeler/api/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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}"
Expand All @@ -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)
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions src/tests/src/solver/modeler/api/mock-fillers/OneVarFiller.h
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
115 changes: 115 additions & 0 deletions src/tests/src/solver/modeler/api/testModelerLPbuilder.cpp
Original file line number Diff line number Diff line change
@@ -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 <https://opensource.org/license/mpl-2-0/>.
*/
#define WIN32_LEAN_AND_MEAN

#include <boost/test/unit_test.hpp>

#include <antares/solver/modeler/api/linearProblemBuilder.h>
#include <antares/solver/modeler/ortoolsImpl/linearProblem.h>

#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<OrtoolsLinearProblem>(false, "sirius");
}

std::vector<LinearProblemFiller*> fillers;
LinearProblemData LP_Data;
std::unique_ptr<ILinearProblem> 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<OneVarFiller>();
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<OneConstraintFiller>();
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<OneVarFiller>();
auto oneConstrFiller = std::make_unique<OneConstraintFiller>();

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<OneVarFiller>();
auto oneConstrFiller = std::make_unique<OneConstraintFiller>();
auto twoVarsTwoConstrFiller = std::make_unique<TwoVarsTwoConstraintsFiller>();
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()
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
* You should have received a copy of the Mozilla Public Licence 2.0
* along with Antares_Simulator. If not, see <https://opensource.org/license/mpl-2-0/>.
*/
#define BOOST_TEST_MODULE test modeler api ortools

#define WIN32_LEAN_AND_MEAN

Expand Down
Loading

0 comments on commit f20ffc8

Please sign in to comment.