dlinear  0.0.1
Delta-complete SMT solver for linear programming
Loading...
Searching...
No Matches
BoundVector.cpp
1
6#include "BoundVector.h"
7
8#include <algorithm>
9#include <ostream>
10#include <utility>
11
12#include "dlinear/util/exception.h"
13
14#define TRACE_VIOLATED_BOUNDS(it) \
15 DLINEAR_TRACE_FMT("BoundVector::ViolatedBounds: ({} {}) incompatible with ({} {})", value, lp_bound, *(it)->value, \
16 (it)->lp_bound)
17
18namespace dlinear {
19
20#define FindLowerBoundValue(value_ptr) bounds_.lower_bound({value_ptr, LpColBound::L, {}})
21#define FindLowerBound(value_ptr, bound) bounds_.lower_bound({value_ptr, bound, {}})
22#define FindUpperBoundValue(value_ptr) bounds_.upper_bound({value_ptr, LpColBound::U, {}})
23#define FindUpperBound(value_ptr, bound) bounds_.upper_bound({value_ptr, bound, {}})
24#define FindLowerNqBoundValue(value_ptr) nq_bounds_.lower_bound({value_ptr, LpColBound::D, {}})
25#define FindUpperNqBoundValue(value_ptr) nq_bounds_.upper_bound({value_ptr, LpColBound::D, {}})
26
27BoundVector::BoundVector(const mpq_class& inf_l, const mpq_class& inf_u)
28 : n_lower_bounds_{0},
29 bounds_{},
30 nq_bounds_{},
31 inf_l_{&inf_l},
32 inf_u_{&inf_u},
33 active_lower_bound_{inf_l_},
34 active_upper_bound_{inf_u_} {}
35
37 return AddBound(*bound.value, bound.lp_bound, bound.theory_literal, bound.explanation);
38}
39BoundIterator BoundVector::AddBound(const mpq_class& value, LpColBound lp_bound, const Literal& theory_lit,
40 const LiteralSet& explanation) {
41 DLINEAR_ASSERT_FMT(lp_bound == LpColBound::L || lp_bound == LpColBound::U || lp_bound == LpColBound::B ||
42 lp_bound == LpColBound::SL || lp_bound == LpColBound::SU || lp_bound == LpColBound::D,
43 "Valid must be L, U, B, SL, SU or D. Received: {}", lp_bound);
44 const BoundIterator violation{ViolatedBounds(value, lp_bound)};
45 if (!violation.empty()) return violation;
46
47 // Add the new lp_bound
48 auto it = bounds_.end();
49 switch (lp_bound) {
50 case LpColBound::SL:
51 case LpColBound::L:
53 it = bounds_.emplace(&value, lp_bound, theory_lit, explanation);
54 break;
55 case LpColBound::SU:
56 case LpColBound::U:
57 it = bounds_.emplace(false, &value, lp_bound, theory_lit, explanation);
58 break;
59 case LpColBound::B:
60 // Check if adding this lp_bound will cause a violation
61 if (ViolatedNqBounds(value, value))
62 return {bounds_.cend(), bounds_.cend(), FindLowerNqBoundValue(&value), FindUpperNqBoundValue(&value)};
65 bounds_.emplace(false, &value, LpColBound::L, theory_lit, explanation);
66 bounds_.emplace(&value, LpColBound::U, theory_lit, explanation);
67 return {};
68 case LpColBound::D:
69 if (IsActiveEquality(value)) return {FindLowerBoundValue(&value), FindUpperBoundValue(&value)};
70 nq_bounds_.emplace(&value, lp_bound, theory_lit, explanation);
71 return {};
72 default:
73 break;
74 }
75
76 bool changed_active_bounds = false;
77 const mpq_class* backup_active_lower_bound = active_lower_bound_;
78 const mpq_class* backup_active_upper_bound = active_upper_bound_;
79 // Check if there has been a change in the active bounds
80 if ((lp_bound == LpColBound::L || lp_bound == LpColBound::SL) && value > *active_lower_bound_) {
81 active_lower_bound_ = &value;
82 changed_active_bounds = true;
83 } else if ((lp_bound == LpColBound::U || lp_bound == LpColBound::SU) && value < *active_upper_bound_) {
84 active_upper_bound_ = &value;
85 changed_active_bounds = true;
86 }
87
88 if (changed_active_bounds) {
89 if (ViolatedNqBounds()) {
90 bounds_.erase(it);
91 if (lp_bound == LpColBound::L || lp_bound == LpColBound::SL) --n_lower_bounds_;
92 const BoundIterator nq_violation{
93 FindLowerBoundValue(active_lower_bound_), FindUpperBoundValue(active_upper_bound_),
94 FindLowerNqBoundValue(active_lower_bound_), FindUpperNqBoundValue(active_upper_bound_)};
95 active_lower_bound_ = backup_active_lower_bound;
96 active_upper_bound_ = backup_active_upper_bound;
97 return nq_violation;
98 }
99 }
100
101 return {};
102}
103bool BoundVector::RemoveBound(const mpq_class& value, const LpColBound lp_bound, const Literal& theory_literal,
104 const LiteralSet& explanation) {
105 return RemoveBound({&value, lp_bound, theory_literal, explanation});
106}
107bool BoundVector::RemoveBound(const Bound& bound) {
108 if (bound.lp_bound == LpColBound::D) {
109 for (auto it = nq_bounds_.find(bound); it != nq_bounds_.end() && *it->value == *bound.value; ++it) {
110 if (it->theory_literal == bound.theory_literal && it->explanation == bound.explanation) {
111 return nq_bounds_.erase(it);
112 }
113 }
114 return false;
115 }
116 if (bound.lp_bound == LpColBound::B) {
117 // Modify the bound temporarily to avoid pointless copies
118 Bound& modified_bound = const_cast<Bound&>(bound);
119 modified_bound.lp_bound = LpColBound::L;
120 const bool l_res = RemoveBound(modified_bound);
121 modified_bound.lp_bound = LpColBound::U;
122 const bool u_res = RemoveBound(modified_bound);
123 // Restore the original lp_bound value
124 modified_bound.lp_bound = LpColBound::B;
125 return l_res || u_res;
126 }
127
128 bool res = false;
129 for (auto it = FindLowerBound(bound.value, bound.lp_bound);
130 it != bounds_.cend() && *it->value == *bound.value && it->lp_bound == bound.lp_bound; ++it) {
131 if (it->theory_literal == bound.theory_literal && it->explanation == bound.explanation) {
132 res = bounds_.erase(it);
133 DLINEAR_ASSERT(res, "Bound must be removed");
134 break;
135 }
136 }
137 if (!res) return res;
138 if (bound.lp_bound == LpColBound::L || bound.lp_bound == LpColBound::SL) {
141 } else {
142 DLINEAR_ASSERT(bound.lp_bound == LpColBound::U || bound.lp_bound == LpColBound::SU, "Invalid bound");
144 }
145 return res;
146}
147BoundIterator BoundVector::ViolatedBounds(const mpq_class& value, LpColBound lp_bound) const {
148 if (lp_bound == LpColBound::D) return {};
149 DLINEAR_ASSERT_FMT(lp_bound == LpColBound::L || lp_bound == LpColBound::U || lp_bound == LpColBound::B ||
150 lp_bound == LpColBound::SL || lp_bound == LpColBound::SU,
151 "Valid must be L, U, B, SL or SU. Received: {}", lp_bound);
152
153 DLINEAR_TRACE_FMT("BoundVector::ViolatedBounds: checking ({} {})", value, lp_bound);
155
156 switch (lp_bound) {
157 case LpColBound::SL:
158 case LpColBound::L:
159 if (value > *active_upper_bound_) return {LowerBoundEnd(), FindUpperBound(&value, !lp_bound)};
160 it = bounds_.upper_bound({&value, lp_bound, {}});
161 if (it == bounds_.cend() || *it->value != value) return {};
162 if (lp_bound == LpColBound::L && it->lp_bound != LpColBound::SU) return {};
163 TRACE_VIOLATED_BOUNDS(it);
164 DLINEAR_ASSERT(LowerBoundEnd() < FindUpperBound(&value, !lp_bound), "Bounds must not be inverted");
165 return {LowerBoundEnd(), FindUpperBound(&value, !lp_bound)};
166 case LpColBound::SU:
167 case LpColBound::U:
168 if (value < *active_lower_bound_) return {FindLowerBound(&value, !lp_bound), LowerBoundEnd()};
169 it = bounds_.lower_bound({&value, lp_bound, {}});
170 if (it == bounds_.cbegin() || *(std::prev(it))->value != value) return {};
171 if (lp_bound == LpColBound::U && (std::prev(it))->lp_bound != LpColBound::SL) return {};
172 TRACE_VIOLATED_BOUNDS((std::prev(it)));
173 DLINEAR_ASSERT(FindLowerBound(&value, !lp_bound) < LowerBoundEnd(), "Bounds must not be inverted");
174 return {FindLowerBound(&value, !lp_bound), LowerBoundEnd()};
175 case LpColBound::B:
176 if (value < *active_lower_bound_) return {FindLowerBound(&value, LpColBound::SL), LowerBoundEnd()};
177 if (value > *active_upper_bound_) return {LowerBoundEnd(), FindUpperBound(&value, LpColBound::SU)};
178 it = bounds_.upper_bound({&value, LpColBound::L, {}});
179 if ((it != bounds_.cend() && *it->value == value && it->lp_bound == LpColBound::SU)) {
180 TRACE_VIOLATED_BOUNDS(it);
181 DLINEAR_ASSERT(LowerBoundEnd() < FindUpperBound(&value, LpColBound::SU), "Bounds must not be inverted");
182 return {LowerBoundEnd(), FindUpperBound(&value, LpColBound::SU)};
183 }
184 it = bounds_.lower_bound({&value, LpColBound::U, {}});
185 if ((it != bounds_.cbegin() && *(std::prev(it))->value == value && (std::prev(it))->lp_bound == LpColBound::SL)) {
186 TRACE_VIOLATED_BOUNDS(std::prev(it));
187 DLINEAR_ASSERT(FindLowerBound(&value, LpColBound::SL) < LowerBoundEnd(), "Bounds must not be inverted");
188 return {FindLowerBound(&value, LpColBound::SL), LowerBoundEnd()};
189 }
190 return {};
191 default:
192 DLINEAR_UNREACHABLE();
193 }
194}
195
197 if (active_upper_bound_ != active_lower_bound_) return false;
198 // The active bounds are equal, verify whether they are violated
199 auto nq_value_it = nq_bounds_.find({active_upper_bound_, LpColBound::D, {}});
200 // No violation, return
201 if (nq_value_it == nq_bounds_.end()) return false;
202 // Violated strict bound, remove the last added bound and return the interval
203 return true;
204}
205
206bool BoundVector::ViolatedNqBounds(const mpq_class& lb, const mpq_class& ub) const {
207 if (lb != ub) return false;
208 // The active bounds are equal, verify whether they are violated
209 auto nq_value_it = nq_bounds_.find({&lb, LpColBound::D, {}});
210 // No violation, return
211 if (nq_value_it == nq_bounds_.end()) return false;
212 // Violated strict bound, remove the last added bound and return the interval
213 return true;
214}
215
223
224bool BoundVector::IsActiveEquality(const mpq_class& value) const {
225 if (n_lower_bounds_ == 0 || n_lower_bounds_ == bounds_.size()) return false;
227}
228
229bool BoundVector::IsLowerBound(const mpq_class& value) const {
230 auto it = bounds_.find({&value, LpColBound::L, {}});
231 if (it != bounds_.cend()) return true;
232 it = bounds_.find({&value, LpColBound::SL, {}});
233 return it != bounds_.cend();
234}
235
236bool BoundVector::IsUpperBound(const mpq_class& value) const {
237 auto it = bounds_.find({&value, LpColBound::U, {}});
238 if (it != bounds_.cend()) return true;
239 it = bounds_.find({&value, LpColBound::SU, {}});
240 return it != bounds_.cend();
241}
242
244 if (active_lower_bound_ == inf_l_ || bounds_.empty() || n_lower_bounds_ == 0) return false;
245 return *active_lower_bound_ > *inf_l_;
246}
247
249 if (active_upper_bound_ == inf_u_ || bounds_.empty() || n_lower_bounds_ == bounds_.size()) return false;
250 return *active_upper_bound_ < *inf_u_;
251}
252
254
256 return bounds_.cbegin() + static_cast<int>(n_lower_bounds_);
257}
258
260BoundIterator BoundVector::GetActiveBound(const mpq_class& value) const { return GetActiveBound(value, value); }
261BoundIterator BoundVector::GetActiveBound(const mpq_class& lb, const mpq_class& ub) const {
262 DLINEAR_ASSERT(lb == ub || (lb == *active_lower_bound_ && ub == *active_upper_bound_), "Bounds must be == or active");
263 DLINEAR_ASSERT(lb <= ub, "Lower bound must be less or equal to upper bound");
264 auto lb_it = FindUpperBound(&lb, LpColBound::SL);
265 auto ub_it = FindLowerBound(&ub, LpColBound::SU);
266 // Adjust the iterators based on the state of the vector
267 if (lb_it != bounds_.cbegin() && lb == *(std::prev(lb_it))->value) lb_it--;
268 if (ub_it != bounds_.cend() && ub == *ub_it->value) ub_it++;
269 return BoundIterator{
270 lb_it, ub_it, // The non-equal bounds become inclusive if there is no normal bounds
271 lb_it == ub_it || lb_it->lp_bound != LpColBound::SL ? FindLowerNqBoundValue(&lb) : FindUpperNqBoundValue(&lb),
272 lb_it == ub_it || (std::prev(ub_it))->lp_bound != LpColBound::SU ? FindUpperNqBoundValue(&ub)
273 : FindLowerNqBoundValue(&ub)};
274}
275
279BoundIterator BoundVector::GetActiveBounds(const mpq_class& value) const { return GetActiveBounds(value, value); }
280BoundIterator BoundVector::GetActiveBounds(const mpq_class& lb, const mpq_class& ub) const {
281 DLINEAR_ASSERT(lb == ub || (lb == *active_lower_bound_ && ub == *active_upper_bound_), "Bounds must be == or active");
282 DLINEAR_ASSERT(lb <= ub, "Lower bound must be less or equal to upper bound");
283 const auto lb_it = FindLowerBoundValue(&lb);
284 const auto ub_it = FindUpperBoundValue(&ub);
285 // The active bounds are empty. Non-equal bounds are inclusive
286 if (lb_it == ub_it) return {lb_it, ub_it, FindLowerNqBoundValue(&lb), FindUpperNqBoundValue(&ub)};
287 const auto& [value_lb, type_lb, lit_lb, exp_lb] = *lb_it;
288 const auto& [value_ub, type_ub, lit_ub, exp_ub] = *(std::prev(ub_it));
289 // The bounds contain only one type of bound or span across multiple values. There is no equality bound
290 if (type_lb != LpColBound::L || type_ub != LpColBound::U || value_lb != value_ub)
291 return {lb_it, ub_it, FindUpperNqBoundValue(&lb), FindLowerNqBoundValue(&ub)};
292
293 auto it = lb_it;
294 auto [value, type, lit, exp] = *it;
295 for (; type != type_ub; ++it, type = it->lp_bound, lit = it->theory_literal) {
296 if (lit == lit_ub) return {it, ub_it, FindUpperNqBoundValue(&lb), FindLowerNqBoundValue(&ub)};
297 }
298 it = std::prev(ub_it);
299 type = it->lp_bound;
300 lit = it->theory_literal;
301 for (; type != type_lb; --it, type = it->lp_bound, lit = it->theory_literal) {
302 if (lit == lit_lb) return {lb_it, std::next(it), FindUpperNqBoundValue(&lb), FindLowerNqBoundValue(&ub)};
303 }
304 return {lb_it, ub_it, FindUpperNqBoundValue(&lb), FindLowerNqBoundValue(&ub)};
305}
306
308 LiteralSet explanation;
309 GetActiveExplanation(explanation);
310 return explanation;
311}
313 for (BoundIterator it = GetActiveBound(); it; ++it) {
314 explanation.insert(it->explanation.cbegin(), it->explanation.cend());
315 explanation.insert(it->theory_literal);
316 }
317}
319 LiteralSet explanation;
320 GetActiveEqExplanation(explanation);
321 return explanation;
322}
324 if (GetActiveEqualityBound() == nullptr) return;
325 for (BoundIterator it = GetActiveBound(); it; ++it) {
326 explanation.insert(it->explanation.cbegin(), it->explanation.cend());
327 explanation.insert(it->theory_literal);
328 }
329}
330
331std::pair<const mpq_class&, const mpq_class&> BoundVector::GetActiveBoundsValue() const {
333}
334
335void BoundVector::SetLowerBound(const mpq_class& value) {
336 if (value > *active_upper_bound_)
337 DLINEAR_RUNTIME_ERROR_FMT("Upper bound must be greater or equal to lower bound. Lower: {}, Upper: {}",
339 if (value > *active_lower_bound_) active_lower_bound_ = &value;
340}
341void BoundVector::SetUpperBound(const mpq_class& value) {
342 if (value < *active_lower_bound_)
343 DLINEAR_RUNTIME_ERROR_FMT("Upper bound must be greater or equal to lower bound. Lower: {}, Upper: {}",
345 if (value < *active_upper_bound_) active_upper_bound_ = &value;
346}
347void BoundVector::SetBounds(const mpq_class& lb, const mpq_class& ub) {
348 if (ub < std::max(lb, *active_lower_bound_) || lb > std::min(ub, *active_upper_bound_))
349 DLINEAR_RUNTIME_ERROR_FMT("Upper bound must be greater or equal to lower bound. Lower: {}, Upper: {}",
353}
354
355std::ostream& operator<<(std::ostream& os, const BoundVector& bounds_vector) {
356 os << "BoundVector[";
357 if (bounds_vector.active_lower_bound() == bounds_vector.inf_l())
358 os << "-∞";
359 else
360 os << bounds_vector.active_lower_bound();
361 os << ", ";
362 if (bounds_vector.active_upper_bound() == bounds_vector.inf_u())
363 os << "∞";
364 else
365 os << bounds_vector.active_upper_bound();
366 os << "]{ ";
367 for (const auto& [value, type, lit, exp] : bounds_vector.bounds()) {
368 for (const Literal& e : exp) os << e << " ";
369 if (!exp.empty()) os << "=> ";
370 os << lit << ": " << *value << "( " << type << " ), ";
371 }
372 os << "}";
373 return os;
374}
375std::ostream& operator<<(std::ostream& os, const BoundVectorMap& bounds_vector_map) {
376 os << "ContextBoundVectorMap{ ";
377 for (const auto& [col, bounds_vector] : bounds_vector_map) {
378 os << "id " << col << ": " << bounds_vector << ", ";
379 }
380 os << "}";
381 return os;
382}
383std::ostream& operator<<(std::ostream& os, const BoundVectorVector& bounds_vector_map) {
384 os << "ContextBoundVectorVector{ ";
385 for (const auto& bounds_vector : bounds_vector_map) {
386 os << bounds_vector << ", ";
387 }
388 os << "}";
389 return os;
390}
391
392} // namespace dlinear
BoundIterator class.
Data structure to store the LP solver bounds in a sorted vector to determine the active lower and upp...
Definition BoundVector.h:48
const mpq_class * active_upper_bound_
Active upper bound.
void SetUpperBound(const mpq_class &value)
Manually set the active upper bound to value.
BoundIterator GetActiveBounds() const
Return a BoundIterator containing a set of bounds enclosing the interval [active_lower_bound_,...
void SetLowerBound(const mpq_class &value)
Manually set the active lower bound to value.
const mpq_class * active_lower_bound_
Active lower bound.
std::size_t n_lower_bounds_
Number of lower bounds, both strict and non-strict.
Bounds nq_bounds_
Non-equality bounds.
const mpq_class * GetActiveEqualityBound() const
Return the active equality bound.
BoundVector(const mpq_class &inf_l, const mpq_class &inf_u)
Construct a new BoundVector using inf_l as the the lower bound and inf_u as the upper bound.
const mpq_class & active_lower_bound() const
Get read-only access to the active_lower_bound of the BoundVector.
bool RemoveBound(const Bound &bound)
Remove bound from the vector if it is present.
Bounds::const_iterator LowerBoundEnd() const
Return an iterator over bounds_ after the last lower bound and to the first upper bound.
BoundIterator AddBound(const Bound &bound)
Add a new bound to the vector.
const mpq_class & active_upper_bound() const
Get read-only access to the active_upper_bound of the BoundVector.
LiteralSet GetActiveExplanation() const
Produce a LiteralSet containing all the active bounds that can be used as an explanation.
bool IsUpperBound(const mpq_class &value) const
Check if value represents an upper bound.
bool IsActiveEquality(const mpq_class &value) const
Check if value represents an equality bound.
bool IsBounded() const
Check if the vector has an upper bound and lower bound.
void Clear()
Clear the vector and reset the active bounds.
const mpq_class * inf_u_
Starting upper bound.
BoundIterator ViolatedBounds(const mpq_class &value, LpColBound lp_bound) const
Check if the combination of value and lp_bound violates any of the existing bounds.
bool IsLowerBound(const mpq_class &value) const
Check if value represents a lower bound.
std::pair< const mpq_class &, const mpq_class & > GetActiveBoundsValue() const
Return a pair containing the active lower and upper bound.
const mpq_class * inf_l_
Starting lower bound.
bool IsLowerBounded() const
Check if the vector has a lower bound greater than inf_l_.
Bounds bounds_
Equality and inequality bounds.
bool IsUpperBounded() const
Check if the vector has an upper bound lower than inf_u_.
const mpq_class & inf_u() const
Get read-only access to the positive infinity bound of the BoundVector.
bool ViolatedNqBounds() const
Check if the active bounds violate any of the existing non-equality bounds.
BoundIterator GetActiveBound() const
Return a BoundIterator containing a minimal set of bounds enclosing the interval [active_lower_bound_...
LiteralSet GetActiveEqExplanation() const
Produce a LiteralSet containing all the active bounds that can be used as an explanation,...
const mpq_class & inf_l() const
Get read-only access to the negative lower bound of the BoundVector.
void SetBounds(const mpq_class &lb, const mpq_class &ub)
Manually set the active bounds to lb and ub.
const_iterator find(const T &value) const
Find the index of an element with the provided value in the sorted list.
bool erase(const const_iterator &it)
Remove the element at position it from the sorted list.
void clear()
Clear the sorted list, removing all elements.
size_t size() const
Get read-only access to the size of the vector.
const_iterator upper_bound(const T &value) const
Find the last position in which an element with the provided value could be inserted without changing...
bool empty() const
Check whether the vector is emtpy.
iterator emplace(Args &&... args)
Emplace a new element into the sorted list.
typename std::vector< Bound >::const_iterator const_iterator
const_iterator lower_bound(const T &value) const
Find the first position in which an element with the provided value could be inserted without changin...
Global namespace for the dlinear library.
LpColBound
Describe the bound of a linear program variable.
Definition LpColBound.h:21
@ U
Upper bound.
@ SL
Strict lower bound.
@ B
Both upper and lower bound are equal (fixed)
@ L
Lower bound.
@ D
Variable must be different from the bound.
@ SU
Strict upper bound.
std::set< Literal > LiteralSet
A set of literals.
Definition literal.h:28
Tuple containing the value, bound type and theory literal associated with the bound.
Definition Bound.h:24
LiteralSet explanation
Explanation for the existence of the bound (e.g. propagation)
Definition Bound.h:37
LpColBound lp_bound
Type of the bound (e.g. L, SL, U, SU)
Definition Bound.h:35
Literal theory_literal
Theory literal associated with the bound.
Definition Bound.h:36
const mpq_class * value
Value of the bound.
Definition Bound.h:34
A literal is a variable with an associated truth value, indicating whether it is true or false.
Definition literal.h:24