aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
LpInterface.cpp
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2026 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2026 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41
48
50
51#ifdef GUM_NO_INLINE
53#endif /* GUM_NO_INLINE */
54
55
56namespace gum {
57 namespace credal {
58 namespace lp {
59
63
64 LpCol::LpCol(unsigned int id) : _id_(id) {
65 GUM_CONSTRUCTOR(LpCol);
66 ;
67 }
68
69 LpCol::LpCol(const LpCol& col) : _id_(col._id_) {
70 GUM_CONS_CPY(LpCol);
71 ;
72 }
73
75 GUM_DESTRUCTOR(LpCol);
76 ;
77 }
78
82
84 _ileft_(false), _imiddle_(false), _iright_(false), _lValue_(0.), _mValue_(0.),
85 _rValue_(0.), _lCoeffs_(new HashTable< LpCol, double >()),
87 GUM_CONSTRUCTOR(LpExpr);
88 }
89
90 LpExpr::LpExpr(const LpExpr& expr) :
95 _rCoeffs_(new HashTable< LpCol, double >(*expr._rCoeffs_)) {
96 GUM_CONS_CPY(LpExpr);
97 }
98
99 LpExpr::LpExpr(const LpExpr& expr, bool copyLeft, bool copyMiddle, bool copyRight) :
100 _ileft_(false), _imiddle_(false), _iright_(false), _lValue_(0.), _mValue_(0.),
101 _rValue_(0.), _lCoeffs_(nullptr), _mCoeffs_(nullptr), _rCoeffs_(nullptr) {
102 if (copyLeft) {
104 _lValue_ = expr._lValue_;
105 _ileft_ = true;
107
108 if (copyMiddle) {
110 _mValue_ = expr._mValue_;
111 _imiddle_ = true;
113
114 if (copyRight) {
116 _rValue_ = expr._rValue_;
117 _iright_ = true;
119
120 GUM_CONS_CPY(LpExpr);
121 }
122
127 expr._lCoeffs_ = nullptr;
128 expr._mCoeffs_ = nullptr;
129 expr._rCoeffs_ = nullptr;
130
131 GUM_CONS_CPY(LpExpr);
132 }
133
134 LpExpr::LpExpr(LpExpr&& expr, bool copyLeft, bool copyMiddle, bool copyRight) :
135 _ileft_(false), _imiddle_(false), _iright_(false), _lValue_(0.), _mValue_(0.),
136 _rValue_(0.), _lCoeffs_(nullptr), _mCoeffs_(nullptr), _rCoeffs_(nullptr) {
137 if (copyLeft) {
138 swap(_lCoeffs_, expr._lCoeffs_);
139 _lValue_ = expr._lValue_;
140 _ileft_ = true;
142
143 if (copyMiddle) {
144 swap(_mCoeffs_, expr._mCoeffs_);
145 _mValue_ = expr._mValue_;
146 _imiddle_ = true;
148
149 if (copyRight) {
150 swap(_rCoeffs_, expr._rCoeffs_);
151 _rValue_ = expr._rValue_;
152 _iright_ = true;
154
155 GUM_CONS_CPY(LpExpr);
156 }
157
159 delete _lCoeffs_;
160 delete _mCoeffs_;
161 delete _rCoeffs_;
162
163 GUM_DESTRUCTOR(LpExpr);
164 }
165
167 clear();
168
169 _mCoeffs_->insert(rhs, 1.);
170 _imiddle_ = true;
171
172 return *this;
173 }
174
177 if (this == &rhs) return *this;
178
179 *_lCoeffs_ = *rhs._lCoeffs_;
180 *_mCoeffs_ = *rhs._mCoeffs_;
181 *_rCoeffs_ = *rhs._rCoeffs_;
182
183 _lValue_ = rhs._lValue_;
184 _mValue_ = rhs._mValue_;
185 _rValue_ = rhs._rValue_;
186
187 _ileft_ = rhs._ileft_;
188 _imiddle_ = rhs._imiddle_;
189 _iright_ = rhs._iright_;
190
191 return *this;
192 }
193
196 if (this == &rhs) return *this;
197
198 swap(_lCoeffs_, rhs._lCoeffs_);
199 swap(_mCoeffs_, rhs._mCoeffs_);
200 swap(_rCoeffs_, rhs._rCoeffs_);
201
202 _lValue_ = rhs._lValue_;
203 _mValue_ = rhs._mValue_;
204 _rValue_ = rhs._rValue_;
205
206 _ileft_ = rhs._ileft_;
207 _imiddle_ = rhs._imiddle_;
208 _iright_ = rhs._iright_;
209
210 return *this;
211 }
212
214 if (_ileft_ || _iright_)
215 GUM_ERROR(OperationNotAllowed, "expr::operator+= (expr) : <= present on one side of expr")
216
217 if (!_imiddle_) _imiddle_ = true;
218
219 _mCoeffs_->getWithDefault(rhs, 0.) += 1.;
220
221 return *this;
222 }
223
225 if (_ileft_ || _iright_ || rhs._ileft_ || rhs._iright_)
227 "expr::operator+= (rhs) : <= present "
228 "on one side of rhs and/or expr");
229
230 if (!_imiddle_) _imiddle_ = true;
231
232 for (const auto& elt: *rhs._mCoeffs_)
233 _mCoeffs_->getWithDefault(elt.first, 0.) += elt.second;
234
235 _mValue_ += rhs._mValue_;
236
237 return *this;
238 }
239
241 if (_ileft_ || _iright_ || rhs._ileft_ || rhs._iright_)
243 "expr::operator+= (rhs) : <= present "
244 "on one side of rhs and/or expr");
245
246 if (!_imiddle_) {
247 _imiddle_ = true;
248 _mValue_ = rhs._mValue_;
249 swap(_mCoeffs_, rhs._mCoeffs_);
250
251 return *this;
252 }
253
254 for (const auto& elt: *rhs._mCoeffs_)
255 _mCoeffs_->getWithDefault(elt.first, 0.) += elt.second;
256 _mValue_ += rhs._mValue_;
257
258 return *this;
259 }
260
262 if (_ileft_ || _iright_)
263 GUM_ERROR(OperationNotAllowed, "expr::operator-= (rhs) : <= present in one of expr")
264
265 if (!_imiddle_) _imiddle_ = true;
266
267 _mCoeffs_->getWithDefault(rhs, 0.) -= 1.;
268
269 return *this;
270 }
271
273 if (_ileft_ || _iright_ || rhs._ileft_ || rhs._iright_)
275 "expr::operator-= (rhs) : <= present in one of rhs and/or expr");
276
277 if (!_imiddle_) _imiddle_ = true;
278
279 for (const auto& elt: *rhs._mCoeffs_)
280 _mCoeffs_->getWithDefault(elt.first, 0.) -= elt.second;
281
282 _mValue_ -= rhs._mValue_;
283
284 return *this;
285 }
286
287 void LpExpr::_addSide_(const LpCol& from) {
288 if (!_ileft_) {
289 _lCoeffs_->insert(from, 1.);
290 _ileft_ = true;
291 } else if (!_imiddle_) {
292 _mCoeffs_->insert(from, 1.);
293 _imiddle_ = true;
294 } else if (!_iright_) {
295 _rCoeffs_->insert(from, 1.);
296 _iright_ = true;
297 } else
299 "LpExpr::setSide ( const LpCol & from "
300 ") : too many <= ; no free side");
301 }
302
303 void LpExpr::_addSide_(const LpExpr& from) {
304 if (_ileft_ && _iright_ && from._imiddle_)
306 "LpExpr::setSide ( const LpCol & from "
307 ") : too many <= ; no free side");
308
310 if (!from._imiddle_) return;
311
315 if (!from._ileft_ && !from._iright_) {
316 if (!_ileft_) {
317 *_lCoeffs_ = *from._mCoeffs_;
318 _lValue_ = from._mValue_;
319 _ileft_ = true;
320
321 return;
322 } else if (!_imiddle_) {
323 *_mCoeffs_ = *from._mCoeffs_;
324 _mValue_ = from._mValue_;
325 _imiddle_ = true;
326
327 return;
328 } else if (!_iright_) {
329 *_rCoeffs_ = *from._mCoeffs_;
330 _rValue_ = from._mValue_;
331 _iright_ = true;
332
333 return;
334 } else
336 "LpExpr::setSide ( const LpCol & from ) "
337 ": too many <= ; no free side");
338 }
341 else if (from._ileft_ && !from._iright_) {
342 if (!_ileft_) {
343 *_lCoeffs_ = *from._lCoeffs_;
344 _lValue_ = from._lValue_;
345 _ileft_ = true;
346
347 *_mCoeffs_ = *from._mCoeffs_;
348 _mValue_ = from._mValue_;
349 _imiddle_ = true;
350
351 return;
352 } else if (!_imiddle_ && !_iright_) {
353 *_mCoeffs_ = *from._lCoeffs_;
354 _mValue_ = from._lValue_;
355 _imiddle_ = true;
356
357 *_rCoeffs_ = *from._mCoeffs_;
358 _rValue_ = from._mValue_;
359 _iright_ = true;
360
361 return;
362 } else
364 "LpExpr::setSide ( const LpCol & from ) "
365 ": too many <= ; no free side");
366 }
369 else if (from._ileft_ && from._iright_) {
370 if (_ileft_ || _imiddle_ || _iright_)
372 "LpExpr::setSide ( const LpCol & from ) "
373 ": too many <= ; no free side");
374
375 *this = from;
376
377 return;
378 } else
380 "LpExpr::setSide ( const LpCol & from "
381 ") : too many <= ; no free side");
382 }
383
386 if (_ileft_ && _iright_ && from._imiddle_)
388 "LpExpr::setSide ( const LpCol & from "
389 ") : too many <= ; no free side");
390
392 if (!from._imiddle_) return;
393
397 if (!from._ileft_ && !from._iright_) {
398 if (!_ileft_) {
400 swap(_lCoeffs_, from._mCoeffs_);
401 _lValue_ = from._mValue_;
402 _ileft_ = true;
403
404 return;
405 } else if (!_imiddle_) {
407 swap(_mCoeffs_, from._mCoeffs_);
408 _mValue_ = from._mValue_;
409 _imiddle_ = true;
410
411 return;
412 } else if (!_iright_) {
414 swap(_rCoeffs_, from._mCoeffs_);
415 _rValue_ = from._mValue_;
416 _iright_ = true;
417
418 return;
419 } else
421 "LpExpr::setSide ( const LpCol & from ) "
422 ": too many <= ; no free side");
423 }
426 else if (from._ileft_ && !from._iright_) {
427 if (!_ileft_) {
429 swap(_lCoeffs_, from._lCoeffs_);
430 _lValue_ = from._lValue_;
431 _ileft_ = true;
432
434 swap(_mCoeffs_, from._mCoeffs_);
435 _mValue_ = from._mValue_;
436 _imiddle_ = true;
437
438 return;
439 } else if (!_imiddle_ && !_iright_) {
441 swap(_mCoeffs_, from._lCoeffs_);
442 _mValue_ = from._lValue_;
443 _imiddle_ = true;
444
446 swap(_rCoeffs_, from._mCoeffs_);
447 _rValue_ = from._mValue_;
448 _iright_ = true;
449
450 return;
451 } else
453 "LpExpr::setSide ( const LpCol & from ) "
454 ": too many <= ; no free side");
455 }
458 else if (from._ileft_ && from._iright_) {
459 if (_ileft_ || _imiddle_ || _iright_)
461 "LpExpr::setSide ( const LpCol & from ) "
462 ": too many <= ; no free side");
463
464 *this = std::move(from);
465
466 return;
467 } else
469 "LpExpr::setSide ( const LpCol & from "
470 ") : too many <= ; no free side");
471 }
472
474 _lCoeffs_->clear();
475 _mCoeffs_->clear();
476 _rCoeffs_->clear();
477
478 _lValue_ = 0.;
479 _mValue_ = 0.;
480 _rValue_ = 0.;
481
482 _ileft_ = false;
483 _imiddle_ = false;
484 _iright_ = false;
485 }
486
487 std::string LpExpr::toString() const {
488 std::string s = "\nleft side : \n";
489
490 if (_lCoeffs_ != nullptr)
491 for (const auto& elt: *_lCoeffs_)
492 s += std::format("{} {} | ", elt.first.toString(), elt.second);
493
494 s += "\nmiddle side : \n";
495
496 if (_mCoeffs_ != nullptr)
497 for (const auto& elt: *_mCoeffs_)
498 s += std::format("{} {} | ", elt.first.toString(), elt.second);
499
500 s += "\nright side : \n";
501
502 if (_rCoeffs_ != nullptr)
503 for (const auto& elt: *_rCoeffs_)
504 s += std::format("{} {} | ", elt.first.toString(), elt.second);
505
506 s += std::format("\nlvalue : {}\nmvalue : {}\nrvalue : {}\n\n",
507 _lValue_,
508 _mValue_,
509 _rValue_);
510
511 return s;
512 }
513
517
518
519 LpRow::LpRow(const LpExpr& expr, const std::vector< LpCol >& cols) : _coeffs_(nullptr) {
520 // we write 0 <= Ax + b from Ex + f <= Cx + d
521 if (expr._ileft_ && !expr._iright_) {
522 _coeffs_ = new HashTable< LpCol, double >(*expr._mCoeffs_);
523
524 for (const auto& col: cols) {
525 double col_coeff = 0.;
526
527 // from left side to middle side : 0 <= middle - left
528 if (expr._lCoeffs_->exists(col)) col_coeff = expr._lCoeffs_->operator[](col);
529
530 _coeffs_->getWithDefault(col, 0.) -= col_coeff;
531 }
532
533 _cste_ = expr._mValue_ - expr._lValue_;
534 } else if (expr._iright_ && !expr._ileft_) {
535 _coeffs_ = new HashTable< LpCol, double >(*expr._rCoeffs_);
536
537 for (const auto& col: cols) {
538 double col_coeff = 0;
539
540 // from middle side to right side : 0 <= right - middle
541 if (expr._mCoeffs_->exists(col)) col_coeff = expr._mCoeffs_->operator[](col);
542
543 _coeffs_->getWithDefault(col, 0.) -= col_coeff;
544 }
545
546 _cste_ = expr._rValue_ - expr._mValue_;
547 } else
549 "expr : " << expr.toString() << "is not a valid inequality; no <= detected");
550
551 if (_coeffs_->size() == 0)
553 "expr : " << expr.toString()
554 << "is not a valid inequality; "
555 "no variable in inequality, "
556 "only constants");
557
558 GUM_CONSTRUCTOR(LpRow);
559 }
560
561 LpRow::LpRow(LpExpr&& expr, const std::vector< LpCol >& cols) : _coeffs_(nullptr) {
563 if (expr._ileft_ && !expr._iright_) {
564 swap(_coeffs_, expr._mCoeffs_);
565
566 for (const auto& col: cols) {
567 double col_coeff = 0;
568
569 if (expr._lCoeffs_->exists(col)) col_coeff = expr._lCoeffs_->operator[](col);
570
571 _coeffs_->getWithDefault(col, 0.) -= col_coeff;
572 }
573
574 _cste_ = expr._mValue_ - expr._lValue_;
575 } else if (expr._iright_ && !expr._ileft_) {
576 swap(_coeffs_, expr._rCoeffs_);
577
578 for (const auto& col: cols) {
579 double col_coeff = 0;
580
581 if (expr._mCoeffs_->exists(col)) col_coeff = expr._mCoeffs_->operator[](col);
582
583 _coeffs_->getWithDefault(col, 0.) -= col_coeff;
584 }
585
586 _cste_ = expr._rValue_ - expr._mValue_;
587 } else
589 "expr : " << expr.toString() << "is not a valid inequality; no <= detected");
590
591 if (_coeffs_->size() == 0)
593 "expr : " << expr.toString()
594 << "is not a valid inequality; "
595 "no variable in inequality, "
596 "only constants");
597
598 GUM_CONSTRUCTOR(LpRow);
599 }
600
601 LpRow::LpRow(const LpRow& row) :
602 _cste_(row._cste_), _coeffs_(new HashTable< LpCol, double >(*row._coeffs_)) {
603 GUM_CONS_CPY(LpRow);
604 }
605
607 row._coeffs_ = nullptr;
608
609 GUM_CONS_CPY(LpRow);
610 }
611
613 delete _coeffs_;
614
615 GUM_DESTRUCTOR(LpRow);
616 }
617
619 _cste_ = row._cste_;
620 *_coeffs_ = *row._coeffs_;
621 return *this;
622 }
623
625 _cste_ = row._cste_;
626 swap(_coeffs_, row._coeffs_);
627 return *this;
628 }
629
630 std::ostream& operator<<(std::ostream& out, const LpRow& row) {
631 out << row.toString();
632 return out;
633 }
634
635 std::string LpRow::toString() const {
636 std::string s = std::format("0 <= {}", _cste_);
637
638 if (_coeffs_ != nullptr) {
639 for (const auto& elt: *_coeffs_) {
640 if (elt.second > 0) {
641 if (elt.second != 1) {
642 s += std::format(" +{}*{}", elt.second, elt.first.toString());
643 } else {
644 s += std::format(" +{}", elt.first.toString());
645 }
646 } else {
647 if (elt.second < 0) {
648 if (elt.second != -1) {
649 s += std::format(" {}*{}", elt.second, elt.first.toString());
650 } else {
651 s += std::format(" -{}", elt.first.toString());
652 }
653 }
654 }
655 }
656 }
657
658 return s;
659 }
660
661 } // namespace lp
662 } // namespace credal
663} // namespace gum
Class representing a polytope ( credal set ) by a set of linear constraints.
Class to include at least once this header.
The class for generic Hash Tables.
Definition hashTable.h:640
Exception : operation not allowed.
Class representing a variable ( a column ) of a linear program, i.e.
Definition LpInterface.h:80
LpCol(unsigned int id)
Default constructor.
unsigned int id() const
Variable id accessor.
~LpCol()
Default destructor.
unsigned int _id_
Variable id.
Class representing a linear expression.
LpExpr & operator+=(const LpCol &rhs)
Compound assignment operator += with a variable.
double _rValue_
The constant on the right side L : L <= M <= R.
bool _ileft_
True if this expression has a non-empty left side L : L <= M <= R .
bool _imiddle_
True if this expression has a non-empty middle side M ( the default ) : L <= M <= R .
LpExpr & operator-=(const LpCol &rhs)
Compound assignment operator -= with a variable.
HashTable< LpCol, double > * _lCoeffs_
The coefficients of each variable on the left side L : L <= M <= R.
LpExpr()
Default constructor.
std::string toString() const
Get the string representation of a calling expression.
void clear()
Clear all data of the calling expression as if it was constructed.
bool _iright_
True if this expression has a non-empty right side R : L <= M <= R .
HashTable< LpCol, double > * _mCoeffs_
The coefficients of each variable on the middle side L : L <= M <= R.
~LpExpr()
Default destructor.
HashTable< LpCol, double > * _rCoeffs_
The coefficients of each variable on the right side L : L <= M <= R.
double _mValue_
The constant on the middle side L : L <= M <= R.
double _lValue_
The constant on the left side L : L <= M <= R.
LpExpr & operator=(const LpCol &rhs)
Assignment operator = with a variable.
void _addSide_(const LpCol &from)
Set the side of the calling expression, from LEFT TO RIGHT : L <= M <= R.
std::string toString() const
Get the string representation of a calling row.
LpRow & operator=(const LpRow &row)
LpRow(const LpExpr &expr, const std::vector< LpCol > &cols)
Constructor from an expression and the address of the vector of variables of the problem.
HashTable< LpCol, double > * _coeffs_
The coefficients of the variables of the linear inequality.
double _cste_
The constant of the linear inequality.
~LpRow()
Default destructor.
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
namespace for constraint-based description of credal sets
Definition agrum.h:64
std::ostream & operator<<(std::ostream &out, const LpRow &row)
void swap(HashTable< LpCol, double > *&a, HashTable< LpCol, double > *&b)
Swap the addresses of two pointers to hashTables.
namespace for all credal networks entities
Definition agrum.h:61
gum is the global namespace for all aGrUM entities
Definition agrum.h:46