12#include "idol/general/utils/Vector.h"
14#include "idol/mixed-integer/modeling/constraints/CtrVersion.h"
15#include "idol/mixed-integer/modeling/variables/VarVersion.h"
16#include "idol/mixed-integer/modeling/expressions/QuadExpr.h"
17#include "idol/general/utils/Point.h"
19#include "idol/mixed-integer/modeling/constraints/Ctr.h"
20#include "idol/mixed-integer/modeling/variables/Var.h"
22#include "idol/general/optimizers/Optimizer.h"
23#include "idol/general/optimizers/OptimizerFactory.h"
25#include "idol/general/optimizers/Timer.h"
26#include "idol/general/utils/LimitedWidthStream.h"
27#include "idol/mixed-integer/modeling/constraints/QCtr.h"
28#include "idol/mixed-integer/modeling/constraints/SOSCtr.h"
31 static const unsigned int MasterId = std::numeric_limits<unsigned int>::max();
45 enum Storage { ColumnOriented, RowOriented, Both };
48 const unsigned int m_id;
49 bool m_has_been_moved =
false;
51 ObjectiveSense m_sense = Minimize;
55 std::vector<Var> m_variables;
56 std::vector<Ctr> m_constraints;
57 std::vector<QCtr> m_qconstraints;
58 std::vector<SOSCtr> m_sosconstraints;
61 mutable bool m_has_minor_representation =
false;
63 std::unique_ptr<Optimizer> m_optimizer;
64 std::unique_ptr<OptimizerFactory> m_optimizer_factory;
66 void throw_if_no_optimizer()
const {
if (!m_optimizer) {
throw Exception(
"No optimizer was found."); } }
70 template<
class T>
void throw_if_unknown_object(
const LinExpr<T>& t_expr);
71 template<
class T>
void throw_if_unknown_object(
const QuadExpr<T>& t_expr);
72 void add_column_to_rows(
const Var& t_var);
73 void add_row_to_columns(
const Ctr& t_ctr);
74 void build_row(
const Ctr& t_ctr);
75 void build_column(
const Var& t_var);
79 bool column_storage_matters()
const;
80 bool row_storage_matters()
const;
82 explicit Model(
Env& t_env, Storage t_storage = Both);
91 Var add_var(
double t_lb,
double t_ub, VarType t_type,
double t_obj = 0., std::string t_name =
"");
93 Var add_var(
double t_lb,
double t_ub, VarType t_type,
double t_obj,
LinExpr<Ctr> t_column, std::string t_name =
"");
95 template<
unsigned int N> Vector<Var, N> add_vars(
Dim<N> t_dim,
double t_lb,
double t_ub, VarType t_type,
double t_obj = 0.,
const std::string& t_name =
"");
97 void add(
const Var& t_var);
99 void add(
const Var& t_var,
TempVar t_temp_var);
101 [[nodiscard]]
bool has(
const Var& t_var)
const;
103 void remove(
const Var& t_var);
107 Ctr add_ctr(
TempCtr t_temp_ctr, std::string t_name =
"");
109 Ctr add_ctr(
LinExpr<Var>&& t_lhs, CtrType t_type,
double t_rhs, std::string t_name =
"");
111 template<
unsigned int N> Vector<Ctr, N> add_ctrs(
Dim<N> t_dim, CtrType t_type,
double t_rhs,
const std::string& t_name =
"");
113 void add(
const Ctr& t_ctr);
115 void add(
const Ctr &t_ctr,
TempCtr t_temp_ctr);
117 [[nodiscard]]
bool has(
const Ctr& t_ctr)
const;
119 void remove(
const Ctr& t_ctr);
123 QCtr add_qctr(
TempQCtr t_temp_ctr, std::string t_name =
"");
127 template<
unsigned int N> Vector<QCtr, N> add_qctrs(
Dim<N> t_dim, CtrType t_type,
const std::string& t_name =
"");
129 void add(
const QCtr& t_ctr);
133 [[nodiscard]]
bool has(
const QCtr& t_ctr)
const;
135 void remove(
const QCtr& t_ctr);
139 SOSCtr add_sosctr(
bool t_is_sos1, std::vector<Var> t_vars, std::vector<double> t_weights, std::string t_name =
"");
141 void add(
const SOSCtr& t_ctr,
bool t_is_sos1,
const std::vector<Var>& t_vars,
const std::vector<double>& t_weights);
143 void add(
const SOSCtr& t_ctr);
145 void remove(
const SOSCtr& t_ctr);
149 [[nodiscard]]
bool has(
const SOSCtr& t_ctr)
const;
151 [[nodiscard]]
unsigned int id()
const {
return m_id; }
153 [[nodiscard]]
Model* clone()
const;
155 [[nodiscard]]
Model copy()
const;
157 void reserve_vars(
unsigned int t_size);
159 void reserve_ctrs(
unsigned int t_size);
161 void reserve_qctrs(
unsigned int t_size);
163 void reserve_sos_ctrs(
unsigned int t_size);
165 [[nodiscard]]
Env& env()
const {
return const_cast<Model*
>(
this)->m_env; }
169 [[nodiscard]]
bool has_optimizer()
const;
171 [[nodiscard]]
bool has_optimizer_factory()
const {
return bool(m_optimizer_factory); }
173 [[nodiscard]]
const OptimizerFactory& optimizer_factory()
const {
return *m_optimizer_factory; }
177 template<
class T,
unsigned int N>
void add_vector(
const Vector<T, N>& t_vector);
179 Optimizer& optimizer() { throw_if_no_optimizer();
return *m_optimizer; }
181 [[nodiscard]]
const Optimizer& optimizer()
const { throw_if_no_optimizer();
return *m_optimizer; }
185 void write(
const std::string& t_name);
189 [[nodiscard]] ObjectiveSense get_obj_sense()
const;
195 [[nodiscard]]
double get_mat_coeff(
const Ctr& t_ctr,
const Var& t_var)
const;
197 [[nodiscard]] SolutionStatus get_status()
const;
199 [[nodiscard]] SolutionReason get_reason()
const;
201 [[nodiscard]]
double get_best_obj()
const;
203 [[nodiscard]]
double get_best_bound()
const;
205 void set_obj_sense(ObjectiveSense t_value);
215 void set_obj_const(
double t_constant);
217 void set_mat_coeff(
const Ctr& t_ctr,
const Var& t_var,
double t_coeff);
219 [[nodiscard]]
QCtr get_qctr_by_index(
unsigned int t_index)
const;
221 [[nodiscard]]
Ctr get_ctr_by_index(
unsigned int t_index)
const;
223 [[nodiscard]]
Var get_var_by_index(
unsigned int t_index)
const;
225 [[nodiscard]]
SOSCtr get_sosctr_by_index(
unsigned int t_index)
const;
227 [[nodiscard]]
unsigned int get_ctr_index(
const Ctr& t_ctr)
const;
229 [[nodiscard]]
unsigned int get_qctr_index(
const QCtr& tctr)
const;
231 [[nodiscard]]
unsigned int get_sosctr_index(
const SOSCtr& t_ctr)
const;
233 [[nodiscard]] CtrType get_ctr_type(
const Ctr& t_ctr)
const;
235 double get_ctr_rhs(
const Ctr& t_ctr)
const;
241 CtrType get_qctr_type(
const QCtr& t_ctr)
const;
243 [[nodiscard]]
double get_ctr_dual(
const Ctr& t_ctr)
const;
245 [[nodiscard]]
double get_ctr_farkas(
const Ctr& t_ctr)
const;
247 void set_ctr_rhs(
const Ctr& t_ctr,
double t_rhs);
249 void set_ctr_type(
const Ctr& t_ctr, CtrType t_type);
255 [[nodiscard]]
unsigned int get_var_index(
const Var& t_var)
const;
257 [[nodiscard]] VarType get_var_type(
const Var& t_var)
const;
259 [[nodiscard]]
double get_var_lb(
const Var& t_var)
const;
261 [[nodiscard]]
double get_var_ub(
const Var& t_var)
const;
263 [[nodiscard]]
double get_var_primal(
const Var& t_var)
const;
265 [[nodiscard]]
double get_var_reduced_cost(
const Var& t_var)
const;
267 [[nodiscard]]
double get_var_ray(
const Var& t_var)
const;
269 [[nodiscard]]
const LinExpr<Ctr>& get_var_column(
const Var& t_var)
const;
271 [[nodiscard]]
double get_var_obj(
const Var& t_var)
const;
273 void set_var_type(
const Var& t_var, VarType t_type);
275 void set_var_lb(
const Var& t_var,
double t_lb);
277 void set_var_ub(
const Var& t_var,
double t_ub);
279 void set_var_obj(
const Var& t_var,
double t_obj);
285 const std::vector<Var>& get_sosctr_vars(
const SOSCtr& t_ctr)
const;
287 const std::vector<double>& get_sosctr_weights(
const SOSCtr& t_ctr)
const;
289 [[nodiscard]]
bool is_sos1(
const SOSCtr& t_ctr)
const;
291 [[nodiscard]]
unsigned int get_n_solutions()
const;
293 [[nodiscard]]
unsigned int get_solution_index()
const;
295 void set_solution_index(
unsigned int t_index);
297 void dump(std::ostream& t_os = std::cout)
const;
299 Storage storage()
const {
return m_storage; }
301 void set_storage(Storage t_storage,
bool t_reset_minor_representation =
false);
303 void reset_minor_representation();
306template<
class T,
unsigned int N>
307void idol::Model::add_vector(
const Vector<T, N> &t_vector) {
308 if constexpr (N == 1) {
309 for (
const auto& x : t_vector) {
313 for (
const auto& x : t_vector) {
314 add_vector<T, N - 1>(x);
319template<
unsigned int N>
320idol::Vector<idol::Var, N> idol::Model::add_vars(Dim<N> t_dim,
double t_lb,
double t_ub, VarType t_type,
double t_obj,
const std::string& t_name) {
321 auto result =
Var::make_vector(m_env, t_dim, t_lb, t_ub, t_type, t_obj, t_name);
322 add_vector<Var, N>(result);
326template<
unsigned int N>
327idol::Vector<idol::Ctr, N> idol::Model::add_ctrs(Dim<N> t_dim, CtrType t_type,
double t_rhs,
const std::string &t_name) {
329 add_vector<Ctr, N>(result);
335 static auto save_primal(
const Model &t_original_model,
const Model &t_model) {
339 const auto status = t_model.get_status();
340 const auto reason = t_model.get_reason();
342 if (status != Optimal && status != Feasible && status != SubOptimal) {
343 throw Exception(
"Primal values not available.");
346 result.set_status(status);
347 result.set_reason(reason);
349 result.set_objective_value(t_model.get_best_obj());
351 for (
const auto &var: t_original_model.vars()) {
352 const double value = t_model.get_var_primal(var);
353 result.set(var, value);
360 static auto save_primal(
const Model &t_original_model) {
361 return save_primal(t_original_model, t_original_model);
364 static auto save_ray(
const Model &t_original_model,
const Model &t_model) {
368 const auto status = t_model.get_status();
369 const auto reason = t_model.get_reason();
371 if (status != Unbounded) {
372 throw Exception(
"Ray not available.");
375 result.set_status(status);
376 result.set_reason(reason);
378 result.set_objective_value(-Inf);
380 for (
const auto &var: t_original_model.vars()) {
381 const double value = t_model.get_var_ray(var);
382 result.set(var, value);
389 static auto save_ray(
const Model &t_original_model) {
390 return save_ray(t_original_model, t_original_model);
393 static auto save_dual(
const Model &t_original_model,
const Model &t_model) {
397 const auto status = t_model.get_status();
398 const auto reason = t_model.get_reason();
400 if (status != Optimal && status != Feasible) {
401 throw Exception(
"Dual values not available.");
404 result.set_status(status);
405 result.set_reason(reason);
407 result.set_objective_value(t_model.get_best_bound());
409 for (
const auto &ctr: t_original_model.ctrs()) {
410 const double value = t_model.get_ctr_dual(ctr);
411 result.set(ctr, value);
418 static auto save_dual(
const Model &t_original_model) {
419 return save_dual(t_original_model, t_original_model);
422 static auto save_farkas(
const Model &t_original_model,
const Model &t_model) {
426 const auto status = t_model.get_status();
427 const auto reason = t_model.get_reason();
429 if (status != Infeasible) {
430 throw Exception(
"Farkas certificate not available.");
433 result.set_status(status);
434 result.set_reason(reason);
436 result.set_objective_value(+Inf);
438 for (
const auto &ctr: t_original_model.ctrs()) {
439 const double value = t_model.get_ctr_farkas(ctr);
440 result.set(ctr, value);
447 static auto save_farkas(
const Model &t_original_model) {
448 return save_farkas(t_original_model, t_original_model);
451 static auto save_reduced_cost(
const Model &t_original_model,
const Model &t_model) {
455 const auto status = t_model.get_status();
456 const auto reason = t_model.get_reason();
458 if (status != Optimal && status != Feasible && status != SubOptimal) {
459 throw Exception(
"Reduced costs not available.");
462 result.set_status(status);
463 result.set_reason(reason);
465 result.set_objective_value(t_model.get_best_obj());
467 for (
const auto &var: t_original_model.vars()) {
468 const double value = t_model.get_var_reduced_cost(var);
469 result.set(var, value);
476 static auto save_reduced_cost(
const Model &t_original_model) {
477 return save_reduced_cost(t_original_model, t_original_model);
484 static std::ostream &operator<<(std::ostream &t_os,
const idol::Model &t_model) {
486 using namespace idol;
488 if (t_model.storage() == Model::Storage::ColumnOriented) {
489 std::cerr <<
"Warning: print a column-oriented model leads to the generation of a minor representation. "
490 "This operation can be slow and memory-consuming." << std::endl;
495 if (t_model.get_obj_sense() == Minimize) {
496 stream <<
"Minimize";
498 stream <<
"Maximize";
501 stream << std::endl <<
"\t" << t_model.get_obj_expr() << std::endl <<
"Subject To" << std::endl;
503 for (
const auto &ctr: t_model.ctrs()) {
505 const auto linear = t_model.get_ctr_row(ctr);
506 const double rhs = t_model.get_ctr_rhs(ctr);
507 const auto type = t_model.get_ctr_type(ctr);
509 stream <<
'\t' << ctr <<
": ";
524 stream <<
" ?undefined? ";
527 stream << rhs << std::endl;
530 for (
const auto& qctr : t_model.qctrs()) {
531 const auto& expr = t_model.get_qctr_expr(qctr);
532 const auto type = t_model.get_qctr_type(qctr);
534 stream <<
'\t' << qctr <<
": ";
549 stream <<
" ?undefined? ";
555 std::list<Var> generals, binaries;
557 stream <<
"Bounds\n";
558 for (
const auto &var: t_model.vars()) {
560 const double lb = t_model.get_var_lb(var);
561 const double ub = t_model.get_var_ub(var);
562 const int type = t_model.get_var_type(var);
566 if (!is_neg_inf(lb) && !is_pos_inf(ub)) {
567 stream << lb <<
" <= " << var <<
" <= " << ub;
568 }
else if (!is_pos_inf(ub)) {
569 stream << var <<
" <= " << ub;
570 }
else if (!is_neg_inf(lb)) {
571 stream << var <<
" >= " << lb;
576 if (type == Binary) {
577 binaries.emplace_back(var);
578 }
else if (type == Integer) {
579 generals.emplace_back(var);
585 if (!generals.empty()) {
586 stream <<
"Generals\n";
587 for (
const auto& var : generals) {
588 stream <<
'\t' << var.name() << std::endl;
592 if (!binaries.empty()) {
593 stream <<
"Binaries\n";
594 for (
const auto& var : binaries) {
595 stream <<
'\t' << var.name() << std::endl;
599 if (t_model.sosctrs().size() > 0) {
601 for (
const auto& sos : t_model.sosctrs()) {
602 stream <<
'\t' << sos <<
": ";
603 const auto& vars = t_model.get_sosctr_vars(sos);
604 const auto& weights = t_model.get_sosctr_weights(sos);
605 for (
unsigned int i = 0; i < vars.size(); ++i) {
606 stream << vars[i] <<
" " << weights[i] <<
" ";
static Vector< Ctr, N - I > make_vector(Env &t_env, const Dim< N > &t_dim, CtrType t_type, double t_constant, const std::string &t_name="")
static Vector< Var, N - I > make_vector(Env &t_env, const Dim< N > &t_dim, double t_lb, double t_ub, VarType t_type, double t_obj, const std::string &t_name="")