aGrUM 2.3.2
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-2025 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-2025 *
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::ostringstream s;
489
490 s << std::endl << "left side : " << std::endl;
491
492 if (_lCoeffs_ != nullptr)
493 for (const auto& elt: *_lCoeffs_)
494 s << elt.first.toString() << " " << elt.second << " | ";
495
496 s << std::endl << "middle side : " << std::endl;
497
498 if (_mCoeffs_ != nullptr)
499 for (const auto& elt: *_mCoeffs_)
500 s << elt.first.toString() << " " << elt.second << " | ";
501
502 s << std::endl << "right side : " << std::endl;
503
504 if (_rCoeffs_ != nullptr)
505 for (const auto& elt: *_rCoeffs_)
506 s << elt.first.toString() << " " << elt.second << " | ";
507
508 s << std::endl
509 << "lvalue : " << _lValue_ << std::endl
510 << "mvalue : " << _mValue_ << std::endl
511 << "rvalue : " << _rValue_ << std::endl;
512
513 s << std::endl;
514
515 return s.str();
516 }
517
521
522
523 LpRow::LpRow(const LpExpr& expr, const std::vector< LpCol >& cols) : _coeffs_(nullptr) {
524 // we write 0 <= Ax + b from Ex + f <= Cx + d
525 if (expr._ileft_ && !expr._iright_) {
526 _coeffs_ = new HashTable< LpCol, double >(*expr._mCoeffs_);
527
528 for (const auto& col: cols) {
529 double col_coeff = 0.;
530
531 // from left side to middle side : 0 <= middle - left
532 if (expr._lCoeffs_->exists(col)) col_coeff = expr._lCoeffs_->operator[](col);
533
534 _coeffs_->getWithDefault(col, 0.) -= col_coeff;
535 }
536
537 _cste_ = expr._mValue_ - expr._lValue_;
538 } else if (expr._iright_ && !expr._ileft_) {
539 _coeffs_ = new HashTable< LpCol, double >(*expr._rCoeffs_);
540
541 for (const auto& col: cols) {
542 double col_coeff = 0;
543
544 // from middle side to right side : 0 <= right - middle
545 if (expr._mCoeffs_->exists(col)) col_coeff = expr._mCoeffs_->operator[](col);
546
547 _coeffs_->getWithDefault(col, 0.) -= col_coeff;
548 }
549
550 _cste_ = expr._rValue_ - expr._mValue_;
551 } else
553 "expr : " << expr.toString() << "is not a valid inequality; no <= detected");
554
555 if (_coeffs_->size() == 0)
557 "expr : " << expr.toString()
558 << "is not a valid inequality; "
559 "no variable in inequality, "
560 "only constants");
561
562 GUM_CONSTRUCTOR(LpRow);
563 }
564
565 LpRow::LpRow(LpExpr&& expr, const std::vector< LpCol >& cols) : _coeffs_(nullptr) {
567 if (expr._ileft_ && !expr._iright_) {
568 swap(_coeffs_, expr._mCoeffs_);
569
570 for (const auto& col: cols) {
571 double col_coeff = 0;
572
573 if (expr._lCoeffs_->exists(col)) col_coeff = expr._lCoeffs_->operator[](col);
574
575 _coeffs_->getWithDefault(col, 0.) -= col_coeff;
576 }
577
578 _cste_ = expr._mValue_ - expr._lValue_;
579 } else if (expr._iright_ && !expr._ileft_) {
580 swap(_coeffs_, expr._rCoeffs_);
581
582 for (const auto& col: cols) {
583 double col_coeff = 0;
584
585 if (expr._mCoeffs_->exists(col)) col_coeff = expr._mCoeffs_->operator[](col);
586
587 _coeffs_->getWithDefault(col, 0.) -= col_coeff;
588 }
589
590 _cste_ = expr._rValue_ - expr._mValue_;
591 } else
593 "expr : " << expr.toString() << "is not a valid inequality; no <= detected");
594
595 if (_coeffs_->size() == 0)
597 "expr : " << expr.toString()
598 << "is not a valid inequality; "
599 "no variable in inequality, "
600 "only constants");
601
602 GUM_CONSTRUCTOR(LpRow);
603 }
604
605 LpRow::LpRow(const LpRow& row) :
606 _cste_(row._cste_), _coeffs_(new HashTable< LpCol, double >(*row._coeffs_)) {
607 GUM_CONS_CPY(LpRow);
608 }
609
611 row._coeffs_ = nullptr;
612
613 GUM_CONS_CPY(LpRow);
614 }
615
617 delete _coeffs_;
618
619 GUM_DESTRUCTOR(LpRow);
620 }
621
623 _cste_ = row._cste_;
624 *_coeffs_ = *row._coeffs_;
625 return *this;
626 }
627
629 _cste_ = row._cste_;
630 swap(_coeffs_, row._coeffs_);
631 return *this;
632 }
633
634 std::ostream& operator<<(std::ostream& out, const LpRow& row) {
635 out << row.toString();
636 return out;
637 }
638
639 std::string LpRow::toString() const {
640 std::ostringstream s;
641
642 s << "0 <= " << _cste_;
643
644 if (_coeffs_ != nullptr) {
645 for (const auto& elt: *_coeffs_) {
646 if (elt.second > 0) {
647 if (elt.second != 1) {
648 s << " +" << elt.second << "*" << elt.first.toString();
649 } else {
650 s << " +" << elt.first.toString();
651 }
652 } else {
653 if (elt.second < 0) {
654 if (elt.second != -1) {
655 s << " " << elt.second << "*" << elt.first.toString();
656 } else {
657 s << " -" << elt.first.toString();
658 }
659 }
660 }
661 }
662 }
663
664 return s.str();
665 }
666
667 } // namespace lp
668 } // namespace credal
669} // 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:637
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:72
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