Skip to content

Commit b2973fa

Browse files
JasonMarechal25JasonMarechalflomnes
authored
Test merge_mps (AntaresSimulatorTeam#1006)
This pull request includes several changes to improve code quality and functionality across multiple files. The most significant changes involve the refactoring of pointer types, the addition of logging and utility functions, and the introduction of new test doubles. ### Add unit tests on Merge_mps ### Refactoring of pointer types: * [`src/cpp/benders/benders_core/CouplingMapGenerator.cpp`](diffhunk://#diff-e5c5805ed18be849a2bcfcaf5f3a459394022162fe8c0b168ad0164688aac93eL25-R25): Changed `logger` parameter type from `std::shared_ptr<ILoggerXpansion>` to `ILoggerXpansion*` in `CouplingMapGenerator::BuildInput`. * [`src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CouplingMapGenerator.h`](diffhunk://#diff-2a07bb398e3d2d00e5899c2b0c02ae4da892bdcf20158651d1a4ce4c1a7b49c3L9-R9): Updated the `logger` parameter type to `ILoggerXpansion*` in the `CouplingMapGenerator` class. ### Logging and utility functions: * [`src/cpp/benders/merge_mps/MergeMPS.cpp`](diffhunk://#diff-62757eadaf6524fb544de56dea6eb06fabf01df2063fbe9c5ca25cf8f0e88e2dR260-R266): Added logging for missing weights in `MergeMPS::slave_weight` and included the `<utility>` header for `std::move`. [[1]](diffhunk://#diff-62757eadaf6524fb544de56dea6eb06fabf01df2063fbe9c5ca25cf8f0e88e2dR260-R266) [[2]](diffhunk://#diff-62757eadaf6524fb544de56dea6eb06fabf01df2063fbe9c5ca25cf8f0e88e2dR4-R27) * [`src/cpp/benders/merge_mps/include/antares-xpansion/benders/merge_mps/MergeMPS.h`](diffhunk://#diff-f8df186d3b07a682ee2f64ddca94670adc233ddbe0d7028c963582afc5c337a9L240-R241): Updated `MergeMPS` constructor to use `std::move` for parameters. ### Introduction of new test doubles: * [`tests/cpp/TestDoubles/InMemoryWriter.h`](diffhunk://#diff-581b2c221bd7bf2f075edab41e8168640770d524440fcf6c62bfce6849fb80f3R1-R63): Added a new `InMemoryWriter` class for testing purposes. * [`tests/cpp/TestDoubles/LoggerStub.h`](diffhunk://#diff-dde293831f9c28d20e0f22df76ec2d420124078045c1b121eb0d72542b784e39R9-R81): Introduced `LoggerNOOPStub` class for logging in tests. * [`tests/cpp/TestDoubles/WriterStub.h`](diffhunk://#diff-000960fa3f240908807b8a51b4e567ceb8f96ced78791f4f6c8d9439e103423fR7-R60): Added `WriterNOOPStub` class for output writing in tests. ### Additional changes: * [`src/python/antares_xpansion/input_checker.py`](diffhunk://#diff-4b22642bb41a146a8d6749fb31a45bc8da1829f8cb9bf9bd939fce1f0032e355R347-L364): Replaced `str_to_bool` function with `is_bool` for checking boolean strings. [[1]](diffhunk://#diff-4b22642bb41a146a8d6749fb31a45bc8da1829f8cb9bf9bd939fce1f0032e355R347-L364) [[2]](diffhunk://#diff-4b22642bb41a146a8d6749fb31a45bc8da1829f8cb9bf9bd939fce1f0032e355L425-R421) * [`tests/cpp/CMakeLists.txt`](diffhunk://#diff-66873f80ca7d40e7be54dd3b7354d3e717f28de60536eec9874fb97ef6d9b4c7R29): Added `merge_mps` to the list of subdirectories in the test configuration. --------- Co-authored-by: Jason Marechal <jason.marechal@gmail.com> Co-authored-by: Florian Omnès <florian.omnes@rte-france.com>
1 parent 931c480 commit b2973fa

28 files changed

Lines changed: 963 additions & 341 deletions

File tree

src/cpp/benders/benders_core/CouplingMapGenerator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct InvalidStructureFile: LogUtils::XpansionError<std::runtime_error>
2222
*responsible for the creation of the structure file.
2323
*/
2424
CouplingMap CouplingMapGenerator::BuildInput(const std::filesystem::path& structure_path,
25-
std::shared_ptr<ILoggerXpansion> logger,
25+
ILoggerXpansion* logger,
2626
const std::string& context)
2727
{
2828
CouplingMap coupling_map;

src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CouplingMapGenerator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ class CouplingMapGenerator
66
{
77
public:
88
static CouplingMap BuildInput(const std::filesystem::path& structure_path,
9-
std::shared_ptr<ILoggerXpansion> logger,
9+
ILoggerXpansion* logger,
1010
const std::string& context = "Benders");
1111
};

src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/common.h

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ enum class MasterFormulation
2929
INTEGER,
3030
RELAXED
3131
};
32+
3233
enum class SOLVER
3334
{
3435
BENDERS,
@@ -43,22 +44,22 @@ typedef std::shared_ptr<Point> PointPtr;
4344

4445
const double EPSILON_PREDICATE = 1e-8;
4546

46-
typedef std::set<std::string> problem_names;
47-
typedef std::map<std::string, int> VariableMap;
48-
typedef std::map<int, std::string> Int2Str;
49-
typedef std::map<std::string, double> Str2Dbl;
50-
typedef std::vector<int> IntVector;
51-
typedef std::vector<char> CharVector;
52-
typedef std::vector<double> DblVector;
53-
typedef std::vector<std::string> StrVector;
54-
typedef std::map<std::string, VariableMap> CouplingMap;
47+
using problem_names = std::set<std::string>;
48+
using VariableMap = std::map<std::string, int>;
49+
using Int2Str = std::map<int, std::string>;
50+
using Str2Dbl = std::map<std::string, double>;
51+
using IntVector = std::vector<int>;
52+
using CharVector = std::vector<char>;
53+
using DblVector = std::vector<double>;
54+
using StrVector = std::vector<std::string>;
55+
using CouplingMap = std::map<std::string, VariableMap>;
5556

56-
typedef std::map<std::string, IntVector> SlaveCutId;
57-
typedef std::tuple<int, std::string, int, bool> ActiveCut;
58-
typedef std::vector<ActiveCut> ActiveCutStorage;
57+
using SlaveCutId = std::map<std::string, IntVector>;
58+
using ActiveCut = std::tuple<int, std::string, int, bool>;
59+
using ActiveCutStorage = std::vector<ActiveCut>;
5960

60-
typedef std::pair<std::string, std::string> mps_coupling;
61-
typedef std::list<mps_coupling> mps_coupling_list;
61+
using mps_coupling = std::pair<std::string, std::string>;
62+
using mps_coupling_list = std::list<mps_coupling>;
6263

6364
enum class BENDERSMETHOD
6465
{
@@ -233,4 +234,5 @@ struct BendersBaseOptions: public BaseOptions
233234
};
234235

235236
void usage(int argc);
237+
236238
Json::Value get_json_file_content(const std::filesystem::path& json_file);

src/cpp/benders/factories/BendersFactory.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ void BendersMainFactory::PrepareForExecution(bool outer_loop)
5151
SetupLoggerAndOutputWriter(benders_options);
5252

5353
const auto coupling_map = CouplingMapGenerator::BuildInput(benders_options.STRUCTURE_FILE,
54-
std::make_shared<BendersLoggerBase>(
55-
benders_loggers_),
54+
&benders_loggers_,
5655
"Benders");
5756

5857
method_ = DeduceBendersMethod(coupling_map.size(), options_.BATCH_SIZE, outer_loop);

src/cpp/benders/merge_mps/MergeMPS.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
#include "antares-xpansion/benders/merge_mps/MergeMPS.h"
22

33
#include <filesystem>
4+
#include <utility>
45

56
#include "antares-xpansion/benders/benders_core/CouplingMapGenerator.h"
67
#include "antares-xpansion/helpers/Timer.h"
78

8-
MergeMPS::MergeMPS(const MergeMPSOptions& options,
9-
Logger& logger,
9+
MergeMPS::MergeMPS(MergeMPSOptions options,
10+
Logger logger,
1011
std::shared_ptr<Output::OutputWriter> writer):
11-
_options(options),
12-
_logger(logger),
13-
_writer(writer)
12+
_options(std::move(options)),
13+
_logger(std::move(logger)),
14+
_writer(std::move(writer))
1415
{
1516
}
1617

18+
/**
19+
* Limitation: on windows may not support master problem with full path as name
20+
*/
1721
void MergeMPS::launch()
1822
{
1923
const auto inputRootDir = std::filesystem::path(_options.INPUTROOT);
2024
auto structure_path(inputRootDir / _options.STRUCTURE_FILE);
21-
CouplingMap input = CouplingMapGenerator::BuildInput(structure_path, _logger, "Merge mps");
25+
CouplingMap input = CouplingMapGenerator::BuildInput(structure_path,
26+
_logger.get(),
27+
"Merge mps");
2228

2329
SolverFactory factory;
2430
std::string solver_to_use = (_options.SOLVER_NAME == "COIN") ? "CBC" : _options.SOLVER_NAME;
@@ -251,6 +257,13 @@ double MergeMPS::slave_weight(int nslaves, const std::string& name) const
251257
}
252258
else
253259
{
260+
if (_options.weights.find(name) == _options.weights.end())
261+
{
262+
_logger->display_message("No weight found for " + name
263+
+ ". Problem will not contribute to objective function",
264+
LogUtils::LOGLEVEL::WARNING,
265+
"MergeMPS");
266+
}
254267
return _options.weights.find(name)->second;
255268
}
256269
}

src/cpp/benders/merge_mps/include/antares-xpansion/benders/merge_mps/MergeMPS.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include "antares-xpansion/benders/benders_core/common.h"
44
#include "antares-xpansion/benders/factories/WriterFactories.h"
5-
#include "antares-xpansion/benders/logger/User.h"
65
#include "antares-xpansion/helpers/solver_utils.h"
76

87
enum Attribute
@@ -47,9 +46,8 @@ enum DblVectorAttribute
4746
MAX_DBL_VECTOR_ATTRIBUTE
4847
};
4948

50-
typedef std::
51-
tuple<IntVector, std::vector<IntVector>, std::vector<CharVector>, std::vector<DblVector>>
52-
raw_standard_lp_data;
49+
using raw_standard_lp_data = std::
50+
tuple<IntVector, std::vector<IntVector>, std::vector<CharVector>, std::vector<DblVector>>;
5351

5452
class StandardLp
5553
{
@@ -237,10 +235,10 @@ class StandardLp
237235
class MergeMPS
238236
{
239237
public:
240-
MergeMPS(const MergeMPSOptions& options,
241-
Logger& logger,
242-
std::shared_ptr<Output::OutputWriter> writer);
238+
MergeMPS(MergeMPSOptions options, Logger logger, std::shared_ptr<Output::OutputWriter> writer);
239+
243240
void launch();
241+
244242
double slave_weight(int nslaves, const std::string& name) const;
245243

246244
MergeMPSOptions _options;

src/cpp/lpnamer/model/include/antares-xpansion/lpnamer/model/Problem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ class Problem: public SolverAbstract
301301
return solver_abstract_->get_splex_num_of_ite_last();
302302
}
303303

304-
void get_lp_sol(double* primals, double* duals, double* reduced_costs) override
304+
void get_lp_sol(double* primals, double* duals, double* reduced_costs) const override
305305
{
306306
solver_abstract_->get_lp_sol(primals, duals, reduced_costs);
307307
}

src/cpp/multisolver_interface/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# Conditionnal settings of solver source files
1515
# ---------------------------------------------------------------------------
1616
list(APPEND Solver_sources
17+
${CMAKE_CURRENT_LIST_DIR}/SolverAbstract.cpp
1718
${CMAKE_CURRENT_LIST_DIR}/SolverFactory.cpp
1819
${CMAKE_CURRENT_LIST_DIR}/SolverConfig.cpp
1920
${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/multisolver_interface/Solver.h
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#include "antares-xpansion/multisolver_interface/SolverAbstract.h"
2+
3+
namespace
4+
{
5+
bool areProblemDimensionsEquals(const SolverAbstract* one, const SolverAbstract* other)
6+
{
7+
return (one->get_ncols() == other->get_ncols()) && (one->get_nrows() == other->get_nrows())
8+
&& (one->get_nelems() == other->get_nelems());
9+
}
10+
11+
bool areObjectiveFunctionEquals(const SolverAbstract* one, const SolverAbstract* other)
12+
{
13+
std::vector<double> obj_one(one->get_ncols());
14+
std::vector<double> obj_other(other->get_ncols());
15+
one->get_obj(obj_one.data(), 0, one->get_ncols() - 1);
16+
other->get_obj(obj_other.data(), 0, other->get_ncols() - 1);
17+
18+
return std::ranges::equal(obj_one, obj_other);
19+
}
20+
21+
template<typename T>
22+
23+
bool IsEqual(const T& lhs, const T& rhs, const T& epsilon = std::numeric_limits<T>::epsilon())
24+
{
25+
if constexpr (std::is_floating_point_v<T>)
26+
{
27+
return std::abs(lhs - rhs) <= epsilon * std::max(std::abs(lhs), std::abs(rhs));
28+
}
29+
else
30+
{
31+
return (lhs == rhs);
32+
}
33+
}
34+
35+
bool areConstraintsEquals(const SolverAbstract* one, const SolverAbstract* other)
36+
{
37+
std::vector<int> mstart(one->get_nrows() + 1);
38+
std::vector<int> cindex(one->get_nelems());
39+
std::vector<double> matval_one(one->get_nelems());
40+
int n_one = 0;
41+
one->get_rows(mstart.data(),
42+
cindex.data(),
43+
matval_one.data(),
44+
one->get_nelems(),
45+
&n_one,
46+
0,
47+
one->get_nrows() - 1);
48+
49+
std::vector<int> mstart_other(other->get_nrows() + 1);
50+
std::vector<int> cindex_other(other->get_nelems());
51+
std::vector<double> matval_other(other->get_nelems());
52+
int n_other = 0;
53+
other->get_rows(mstart_other.data(),
54+
cindex_other.data(),
55+
matval_other.data(),
56+
other->get_nelems(),
57+
&n_other,
58+
0,
59+
other->get_nrows() - 1);
60+
61+
if (n_one != n_other)
62+
{
63+
return false;
64+
}
65+
return std::ranges::equal(mstart, mstart_other) && std::ranges::equal(cindex, cindex_other)
66+
&& std::ranges::equal(matval_one,
67+
matval_other,
68+
[](auto l, auto r) { return IsEqual(l, r); });
69+
}
70+
71+
bool areRHSEquals(const SolverAbstract* one, const SolverAbstract* other)
72+
{
73+
std::vector<double> rhs_one(one->get_nrows());
74+
std::vector<double> rhs_other(other->get_nrows());
75+
one->get_rhs(rhs_one.data(), 0, one->get_nrows() - 1);
76+
other->get_rhs(rhs_other.data(), 0, other->get_nrows() - 1);
77+
78+
return std::ranges::equal(rhs_one, rhs_other, [](auto l, auto r) { return IsEqual(l, r); });
79+
}
80+
81+
bool areRowTypesEquals(const SolverAbstract* one, const SolverAbstract* other)
82+
{
83+
std::vector<char> order_one(one->get_nrows());
84+
std::vector<char> order_other(other->get_nrows());
85+
one->get_row_type(order_one.data(), 0, one->get_nrows() - 1);
86+
other->get_row_type(order_other.data(), 0, other->get_nrows() - 1);
87+
88+
return std::ranges::equal(order_one, order_other);
89+
}
90+
91+
bool areBoundsEquals(const SolverAbstract* one, const SolverAbstract* other)
92+
{
93+
std::vector<double> lb_one(one->get_ncols());
94+
std::vector<double> lb_other(other->get_ncols());
95+
one->get_lb(lb_one.data(), 0, one->get_ncols() - 1);
96+
other->get_lb(lb_other.data(), 0, other->get_ncols() - 1);
97+
98+
if (!std::ranges::equal(lb_one, lb_other, [](auto l, auto r) { return IsEqual(l, r); }))
99+
{
100+
return false;
101+
}
102+
103+
std::vector<double> ub_one(one->get_ncols());
104+
std::vector<double> ub_other(other->get_ncols());
105+
one->get_ub(ub_one.data(), 0, one->get_ncols() - 1);
106+
other->get_ub(ub_other.data(), 0, other->get_ncols() - 1);
107+
108+
if (!std::ranges::equal(ub_one, ub_other, [](auto l, auto r) { return IsEqual(l, r); }))
109+
{
110+
return false;
111+
}
112+
return true;
113+
}
114+
} // namespace
115+
116+
bool SolverAbstract::operator==(const SolverAbstract& other) const
117+
{
118+
if (this == &other)
119+
{
120+
return true;
121+
}
122+
bool is_equal = true;
123+
is_equal = is_equal && areProblemDimensionsEquals(this, &other);
124+
is_equal = is_equal && areObjectiveFunctionEquals(this, &other);
125+
is_equal = is_equal && areConstraintsEquals(this, &other);
126+
is_equal = is_equal && areRHSEquals(this, &other);
127+
is_equal = is_equal && areRowTypesEquals(this, &other);
128+
is_equal = is_equal && areBoundsEquals(this, &other);
129+
return is_equal;
130+
}

src/cpp/multisolver_interface/SolverCbc.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ int SolverCbc::get_splex_num_of_ite_last() const
791791
return _cbc.solver()->getIterationCount();
792792
}
793793

794-
void SolverCbc::get_lp_sol(double* primals, double* duals, double* reduced_costs)
794+
void SolverCbc::get_lp_sol(double* primals, double* duals, double* reduced_costs) const
795795
{
796796
if (primals != nullptr)
797797
{

0 commit comments

Comments
 (0)