idol
A C++ Framework for Optimization
Loading...
Searching...
No Matches
Model.h
1//
2// Created by henri on 27/01/23.
3//
4
5#ifndef IDOL_MODEL_H
6#define IDOL_MODEL_H
7
8#include <vector>
9#include <string>
10#include <functional>
11
12#include "idol/general/utils/Vector.h"
13
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"
18
19#include "idol/mixed-integer/modeling/constraints/Ctr.h"
20#include "idol/mixed-integer/modeling/variables/Var.h"
21
22#include "idol/general/optimizers/Optimizer.h"
23#include "idol/general/optimizers/OptimizerFactory.h"
24
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"
29
30namespace idol {
31 static const unsigned int MasterId = std::numeric_limits<unsigned int>::max();
32
33 class Env;
34 class TempCtr;
35
36 class Model;
37
38}
39
44public:
45 enum Storage { ColumnOriented, RowOriented, Both };
46private:
47 Env& m_env;
48 const unsigned int m_id;
49 bool m_has_been_moved = false;
50
51 ObjectiveSense m_sense = Minimize;
52
53 QuadExpr<Var> m_objective;
54 LinExpr<Ctr> m_rhs;
55 std::vector<Var> m_variables;
56 std::vector<Ctr> m_constraints;
57 std::vector<QCtr> m_qconstraints;
58 std::vector<SOSCtr> m_sosconstraints;
59
60 Storage m_storage;
61 mutable bool m_has_minor_representation = false;
62
63 std::unique_ptr<Optimizer> m_optimizer;
64 std::unique_ptr<OptimizerFactory> m_optimizer_factory;
65
66 void throw_if_no_optimizer() const { if (!m_optimizer) { throw Exception("No optimizer was found."); } }
67
68 Model(const Model& t_src);
69
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);
76 void build_rows();
77 void build_columns();
78
79 bool column_storage_matters() const;
80 bool row_storage_matters() const;
81public:
82 explicit Model(Env& t_env, Storage t_storage = Both);
83
84 Model(Model&&) noexcept;
85
86 Model& operator=(const Model&) = delete;
87 Model& operator=(Model&&) noexcept = delete;
88
89 ~Model();
90
91 Var add_var(double t_lb, double t_ub, VarType t_type, double t_obj = 0., std::string t_name = "");
92
93 Var add_var(double t_lb, double t_ub, VarType t_type, double t_obj, LinExpr<Ctr> t_column, std::string t_name = "");
94
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 = "");
96
97 void add(const Var& t_var);
98
99 void add(const Var& t_var, TempVar t_temp_var);
100
101 [[nodiscard]] bool has(const Var& t_var) const;
102
103 void remove(const Var& t_var);
104
105 [[nodiscard]] ConstIteratorForward<std::vector<Var>> vars() const { return m_variables; }
106
107 Ctr add_ctr(TempCtr t_temp_ctr, std::string t_name = "");
108
109 Ctr add_ctr(LinExpr<Var>&& t_lhs, CtrType t_type, double t_rhs, std::string t_name = "");
110
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 = "");
112
113 void add(const Ctr& t_ctr);
114
115 void add(const Ctr &t_ctr, TempCtr t_temp_ctr);
116
117 [[nodiscard]] bool has(const Ctr& t_ctr) const;
118
119 void remove(const Ctr& t_ctr);
120
121 [[nodiscard]] ConstIteratorForward<std::vector<Ctr>> ctrs() const { return m_constraints; }
122
123 QCtr add_qctr(TempQCtr t_temp_ctr, std::string t_name = "");
124
125 QCtr add_qctr(QuadExpr<Var>&& t_expr, CtrType t_type, std::string t_name = "");
126
127 template<unsigned int N> Vector<QCtr, N> add_qctrs(Dim<N> t_dim, CtrType t_type, const std::string& t_name = "");
128
129 void add(const QCtr& t_ctr);
130
131 void add(const QCtr &t_ctr, TempQCtr t_temp_ctr);
132
133 [[nodiscard]] bool has(const QCtr& t_ctr) const;
134
135 void remove(const QCtr& t_ctr);
136
137 [[nodiscard]] ConstIteratorForward<std::vector<QCtr>> qctrs() const { return m_qconstraints; }
138
139 SOSCtr add_sosctr(bool t_is_sos1, std::vector<Var> t_vars, std::vector<double> t_weights, std::string t_name = "");
140
141 void add(const SOSCtr& t_ctr, bool t_is_sos1, const std::vector<Var>& t_vars, const std::vector<double>& t_weights);
142
143 void add(const SOSCtr& t_ctr);
144
145 void remove(const SOSCtr& t_ctr);
146
147 [[nodiscard]] ConstIteratorForward<std::vector<SOSCtr>> sosctrs() const { return m_sosconstraints; }
148
149 [[nodiscard]] bool has(const SOSCtr& t_ctr) const;
150
151 [[nodiscard]] unsigned int id() const { return m_id; }
152
153 [[nodiscard]] Model* clone() const;
154
155 [[nodiscard]] Model copy() const;
156
157 void reserve_vars(unsigned int t_size);
158
159 void reserve_ctrs(unsigned int t_size);
160
161 void reserve_qctrs(unsigned int t_size);
162
163 void reserve_sos_ctrs(unsigned int t_size);
164
165 [[nodiscard]] Env& env() const { return const_cast<Model*>(this)->m_env; }
166
167 void use(const OptimizerFactory& t_optimizer_factory);
168
169 [[nodiscard]] bool has_optimizer() const;
170
171 [[nodiscard]] bool has_optimizer_factory() const { return bool(m_optimizer_factory); }
172
173 [[nodiscard]] const OptimizerFactory& optimizer_factory() const { return *m_optimizer_factory; }
174
175 void unuse();
176
177 template<class T, unsigned int N> void add_vector(const Vector<T, N>& t_vector);
178
179 Optimizer& optimizer() { throw_if_no_optimizer(); return *m_optimizer; }
180
181 [[nodiscard]] const Optimizer& optimizer() const { throw_if_no_optimizer(); return *m_optimizer; }
182
183 void optimize();
184
185 void write(const std::string& t_name);
186
187 void update();
188
189 [[nodiscard]] ObjectiveSense get_obj_sense() const;
190
191 [[nodiscard]] const QuadExpr<Var>& get_obj_expr() const;
192
193 [[nodiscard]] const LinExpr<Ctr>& get_rhs_expr() const;
194
195 [[nodiscard]] double get_mat_coeff(const Ctr& t_ctr, const Var& t_var) const;
196
197 [[nodiscard]] SolutionStatus get_status() const;
198
199 [[nodiscard]] SolutionReason get_reason() const;
200
201 [[nodiscard]] double get_best_obj() const;
202
203 [[nodiscard]] double get_best_bound() const;
204
205 void set_obj_sense(ObjectiveSense t_value);
206
207 void set_obj_expr(const QuadExpr<Var>& t_objective);
208
209 void set_obj_expr(QuadExpr<Var>&& t_objective);
210
211 void set_rhs_expr(LinExpr<Ctr>&& t_rhs);
212
213 void set_rhs_expr(const LinExpr<Ctr>& t_rhs);
214
215 void set_obj_const(double t_constant);
216
217 void set_mat_coeff(const Ctr& t_ctr, const Var& t_var, double t_coeff);
218
219 [[nodiscard]] QCtr get_qctr_by_index(unsigned int t_index) const;
220
221 [[nodiscard]] Ctr get_ctr_by_index(unsigned int t_index) const;
222
223 [[nodiscard]] Var get_var_by_index(unsigned int t_index) const;
224
225 [[nodiscard]] SOSCtr get_sosctr_by_index(unsigned int t_index) const;
226
227 [[nodiscard]] unsigned int get_ctr_index(const Ctr& t_ctr) const;
228
229 [[nodiscard]] unsigned int get_qctr_index(const QCtr& tctr) const;
230
231 [[nodiscard]] unsigned int get_sosctr_index(const SOSCtr& t_ctr) const;
232
233 [[nodiscard]] CtrType get_ctr_type(const Ctr& t_ctr) const;
234
235 double get_ctr_rhs(const Ctr& t_ctr) const;
236
237 [[nodiscard]] const LinExpr<Var>& get_ctr_row(const Ctr& t_ctr) const;
238
239 const QuadExpr<Var>& get_qctr_expr(const QCtr& t_ctr) const;
240
241 CtrType get_qctr_type(const QCtr& t_ctr) const;
242
243 [[nodiscard]] double get_ctr_dual(const Ctr& t_ctr) const;
244
245 [[nodiscard]] double get_ctr_farkas(const Ctr& t_ctr) const;
246
247 void set_ctr_rhs(const Ctr& t_ctr, double t_rhs);
248
249 void set_ctr_type(const Ctr& t_ctr, CtrType t_type);
250
251 void set_ctr_row(const Ctr& t_ctr, const LinExpr<Var>& t_row);
252
253 void set_ctr_row(const Ctr& t_ctr, LinExpr<Var>&& t_row);
254
255 [[nodiscard]] unsigned int get_var_index(const Var& t_var) const;
256
257 [[nodiscard]] VarType get_var_type(const Var& t_var) const;
258
259 [[nodiscard]] double get_var_lb(const Var& t_var) const;
260
261 [[nodiscard]] double get_var_ub(const Var& t_var) const;
262
263 [[nodiscard]] double get_var_primal(const Var& t_var) const;
264
265 [[nodiscard]] double get_var_reduced_cost(const Var& t_var) const;
266
267 [[nodiscard]] double get_var_ray(const Var& t_var) const;
268
269 [[nodiscard]] const LinExpr<Ctr>& get_var_column(const Var& t_var) const;
270
271 [[nodiscard]] double get_var_obj(const Var& t_var) const;
272
273 void set_var_type(const Var& t_var, VarType t_type);
274
275 void set_var_lb(const Var& t_var, double t_lb);
276
277 void set_var_ub(const Var& t_var, double t_ub);
278
279 void set_var_obj(const Var& t_var, double t_obj);
280
281 void set_var_column(const Var& t_var, const LinExpr<Ctr>& t_column);
282
283 void set_var_column(const Var& t_var, LinExpr<Ctr>&& t_column);
284
285 const std::vector<Var>& get_sosctr_vars(const SOSCtr& t_ctr) const;
286
287 const std::vector<double>& get_sosctr_weights(const SOSCtr& t_ctr) const;
288
289 [[nodiscard]] bool is_sos1(const SOSCtr& t_ctr) const;
290
291 [[nodiscard]] unsigned int get_n_solutions() const;
292
293 [[nodiscard]] unsigned int get_solution_index() const;
294
295 void set_solution_index(unsigned int t_index);
296
297 void dump(std::ostream& t_os = std::cout) const;
298
299 Storage storage() const { return m_storage; }
300
301 void set_storage(Storage t_storage, bool t_reset_minor_representation = false);
302
303 void reset_minor_representation();
304};
305
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) {
310 add(x);
311 }
312 } else {
313 for (const auto& x : t_vector) {
314 add_vector<T, N - 1>(x);
315 }
316 }
317}
318
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);
323 return result;
324}
325
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) {
328 auto result = Ctr::make_vector(m_env, t_dim, t_type, t_rhs, t_name);
329 add_vector<Ctr, N>(result);
330 return result;
331}
332
333namespace idol {
334
335 static auto save_primal(const Model &t_original_model, const Model &t_model) {
336
337 PrimalPoint result;
338
339 const auto status = t_model.get_status();
340 const auto reason = t_model.get_reason();
341
342 if (status != Optimal && status != Feasible && status != SubOptimal) {
343 throw Exception("Primal values not available.");
344 }
345
346 result.set_status(status);
347 result.set_reason(reason);
348
349 result.set_objective_value(t_model.get_best_obj());
350
351 for (const auto &var: t_original_model.vars()) {
352 const double value = t_model.get_var_primal(var);
353 result.set(var, value);
354 }
355
356 return result;
357
358 }
359
360 static auto save_primal(const Model &t_original_model) {
361 return save_primal(t_original_model, t_original_model);
362 }
363
364 static auto save_ray(const Model &t_original_model, const Model &t_model) {
365
366 PrimalPoint result;
367
368 const auto status = t_model.get_status();
369 const auto reason = t_model.get_reason();
370
371 if (status != Unbounded) {
372 throw Exception("Ray not available.");
373 }
374
375 result.set_status(status);
376 result.set_reason(reason);
377
378 result.set_objective_value(-Inf);
379
380 for (const auto &var: t_original_model.vars()) {
381 const double value = t_model.get_var_ray(var);
382 result.set(var, value);
383 }
384
385 return result;
386
387 }
388
389 static auto save_ray(const Model &t_original_model) {
390 return save_ray(t_original_model, t_original_model);
391 }
392
393 static auto save_dual(const Model &t_original_model, const Model &t_model) {
394
395 DualPoint result;
396
397 const auto status = t_model.get_status();
398 const auto reason = t_model.get_reason();
399
400 if (status != Optimal && status != Feasible) {
401 throw Exception("Dual values not available.");
402 }
403
404 result.set_status(status);
405 result.set_reason(reason);
406
407 result.set_objective_value(t_model.get_best_bound());
408
409 for (const auto &ctr: t_original_model.ctrs()) {
410 const double value = t_model.get_ctr_dual(ctr);
411 result.set(ctr, value);
412 }
413
414 return result;
415
416 }
417
418 static auto save_dual(const Model &t_original_model) {
419 return save_dual(t_original_model, t_original_model);
420 }
421
422 static auto save_farkas(const Model &t_original_model, const Model &t_model) {
423
424 DualPoint result;
425
426 const auto status = t_model.get_status();
427 const auto reason = t_model.get_reason();
428
429 if (status != Infeasible) {
430 throw Exception("Farkas certificate not available.");
431 }
432
433 result.set_status(status);
434 result.set_reason(reason);
435
436 result.set_objective_value(+Inf);
437
438 for (const auto &ctr: t_original_model.ctrs()) {
439 const double value = t_model.get_ctr_farkas(ctr);
440 result.set(ctr, value);
441 }
442
443 return result;
444
445 }
446
447 static auto save_farkas(const Model &t_original_model) {
448 return save_farkas(t_original_model, t_original_model);
449 }
450
451 static auto save_reduced_cost(const Model &t_original_model, const Model &t_model) {
452
453 PrimalPoint result;
454
455 const auto status = t_model.get_status();
456 const auto reason = t_model.get_reason();
457
458 if (status != Optimal && status != Feasible && status != SubOptimal) {
459 throw Exception("Reduced costs not available.");
460 }
461
462 result.set_status(status);
463 result.set_reason(reason);
464
465 result.set_objective_value(t_model.get_best_obj());
466
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);
470 }
471
472 return result;
473
474 }
475
476 static auto save_reduced_cost(const Model &t_original_model) {
477 return save_reduced_cost(t_original_model, t_original_model);
478 }
479
480}
481
482namespace idol {
483
484 static std::ostream &operator<<(std::ostream &t_os, const idol::Model &t_model) {
485
486 using namespace idol;
487
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;
491 }
492
493 LimitedWidthStream stream(t_os, 120);
494
495 if (t_model.get_obj_sense() == Minimize) {
496 stream << "Minimize";
497 } else {
498 stream << "Maximize";
499 }
500
501 stream << std::endl << "\t" << t_model.get_obj_expr() << std::endl << "Subject To" << std::endl;
502
503 for (const auto &ctr: t_model.ctrs()) {
504
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);
508
509 stream << '\t' << ctr << ": ";
510
511 stream << linear;
512
513 switch (type) {
514 case LessOrEqual:
515 stream << " <= ";
516 break;
517 case Equal:
518 stream << " = ";
519 break;
520 case GreaterOrEqual:
521 stream << " >= ";
522 break;
523 default:
524 stream << " ?undefined? ";
525 }
526
527 stream << rhs << std::endl;
528 }
529
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);
533
534 stream << '\t' << qctr << ": ";
535
536 stream << expr;
537
538 switch (type) {
539 case LessOrEqual:
540 stream << " <= 0";
541 break;
542 case Equal:
543 stream << " = 0";
544 break;
545 case GreaterOrEqual:
546 stream << " >= 0";
547 break;
548 default:
549 stream << " ?undefined? ";
550 }
551
552 stream << std::endl;
553 }
554
555 std::list<Var> generals, binaries;
556
557 stream << "Bounds\n";
558 for (const auto &var: t_model.vars()) {
559
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);
563
564 stream << '\t';
565
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;
572 } else {
573 stream << var;
574 }
575
576 if (type == Binary) {
577 binaries.emplace_back(var);
578 } else if (type == Integer) {
579 generals.emplace_back(var);
580 }
581
582 stream << std::endl;
583 }
584
585 if (!generals.empty()) {
586 stream << "Generals\n";
587 for (const auto& var : generals) {
588 stream << '\t' << var.name() << std::endl;
589 }
590 }
591
592 if (!binaries.empty()) {
593 stream << "Binaries\n";
594 for (const auto& var : binaries) {
595 stream << '\t' << var.name() << std::endl;
596 }
597 }
598
599 if (t_model.sosctrs().size() > 0) {
600 stream << "SOS\n";
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] << " ";
607 }
608 stream << std::endl;
609 }
610 }
611
612 return t_os;
613 }
614
615}
616
617#endif //IDOL_MODEL_H
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="")
Definition Ctr.h:92
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="")
Definition Var.h:104