dlinear  0.0.1
Delta-complete SMT solver for linear programming
Loading...
Searching...
No Matches
Box.cpp
1
7#include "Box.h"
8
9#include <algorithm>
10#include <cfenv>
11#include <cstddef>
12#include <initializer_list>
13#include <limits>
14#include <ostream>
15
16#include "dlinear/util/Infinity.h"
17#include "dlinear/util/RoundingModeGuard.hpp"
18#include "dlinear/util/exception.h"
19#include "dlinear/util/math.h"
20
21namespace dlinear {
22
23using gmp::ceil;
24using gmp::floor;
25
26Box::Box(const Config::LPSolver lp_solver)
27 : lp_solver_{lp_solver},
28 values_{},
29 variables_{std::make_shared<std::vector<Variable>>()},
30 var_to_idx_{std::make_shared<std::unordered_map<Variable, int>>()},
31 idx_to_var_{std::make_shared<std::unordered_map<int, Variable>>()} {}
32
33Box::Box(const std::vector<Variable> &variables, const Config::LPSolver lp_solver) : Box{lp_solver} {
34 values_.reserve(variables.size());
35 variables_->reserve(variables.size());
36 for (const Variable &var : variables) Add(var);
37}
38
39void Box::Add(const Variable &v) {
40 if (v.get_type() == Variable::Type::BINARY || v.get_type() == Variable::Type::INTEGER) {
41 DLINEAR_RUNTIME_ERROR("Integer variables not supported");
42 }
43
44 // Duplicate variables are not allowed.
45 DLINEAR_ASSERT(std::find_if(variables_->begin(), variables_->end(),
46 [&v](const Variable &var) { return v.equal_to(var); }) == variables_->end(),
47 "Duplicate variables are not allowed");
48
49 if (!variables_.unique()) {
50 // If the components of this box is shared by more than one
51 // entity, we need to clone this before adding the variable `v`
52 // so that these changes remain local.
53 variables_ = std::make_shared<std::vector<Variable>>(*variables_);
54 var_to_idx_ = std::make_shared<std::unordered_map<Variable, int>>(*var_to_idx_);
55 idx_to_var_ = std::make_shared<std::unordered_map<int, Variable>>(*idx_to_var_);
56 }
57 const int n{size()};
58 variables_->push_back(v);
59 var_to_idx_->emplace(v, n);
60 idx_to_var_->emplace(n, v);
61
62 // Set up Domain.
63 // TODO(soonho): For now, we allow Boolean variables in a box. Change this.
64 switch (v.get_type()) {
67 values_.emplace_back(0, 1);
68 break;
70 DLINEAR_RUNTIME_ERROR("Integer variables not supported");
71 values_.emplace_back(-std::numeric_limits<int>::max(), std::numeric_limits<int>::max());
72 break;
75 }
76}
77
78void Box::Add(const Variable &v, const mpq_class &lb, const mpq_class &ub) {
79 Add(v);
80
81 DLINEAR_ASSERT(lb <= ub, "Lower bound must be less than or equal to upper bound");
82
83 // Binary variable => lb, ub ∈ [0, 1].
84 DLINEAR_ASSERT(v.get_type() != Variable::Type::BINARY || (0.0 <= lb && ub <= 1.0),
85 "Binary variable must be in [0, 1]");
86
87 // Integer variable => lb, ub ∈ Z.
88 DLINEAR_ASSERT(v.get_type() != Variable::Type::INTEGER || (IsInteger(lb) && IsInteger(ub)),
89 "Integer variable must be in Z");
90
91 values_[(*var_to_idx_)[v]] = Interval{lb, ub};
92}
93
94bool Box::empty() const { return values_.empty() || values_.front().is_empty(); }
95
97 for (Interval &iv : values_) iv.set_empty();
98}
99
100int Box::size() const { return static_cast<int>(variables_->size()); }
101
103 DLINEAR_ASSERT(i < size(), "Index out of bound");
104 return values_[i];
105}
106Interval &Box::operator[](const Variable &var) { return values_[index(var)]; }
107const Interval &Box::operator[](const int i) const {
108 DLINEAR_ASSERT(i < size(), "Index out of bound");
109 return values_[i];
110}
111const Interval &Box::operator[](const Variable &var) const { return values_[index(var)]; }
112
113const std::vector<Variable> &Box::variables() const { return *variables_; }
114
115const Variable &Box::variable(const int i) const { return idx_to_var_->at(i); }
116
117bool Box::has_variable(const Variable &var) const { return var_to_idx_->count(var) > 0; }
118
119int Box::index(const Variable &var) const { return var_to_idx_->at(var); }
120
121const std::vector<Interval> &Box::interval_vector() const { return values_; }
122std::vector<Interval> &Box::m_interval_vector() { return values_; }
123
124std::pair<mpq_class, int> Box::MaxDiam() const {
125 mpq_class max_diam{0.0};
126 int idx{-1};
127 for (std::size_t i{0}; i < variables_->size(); ++i) {
128 const mpq_class &diam_i{values_[i].diam()};
129 if (diam_i > max_diam && values_[i].is_bisectable()) {
130 max_diam = diam_i;
131 idx = i;
132 }
133 }
134 return std::make_pair(max_diam, idx);
135}
136
137std::pair<Box, Box> Box::Bisect(const int i) const {
138 const Variable &var{(*idx_to_var_)[i]};
139 if (!values_[i].is_bisectable()) {
140 DLINEAR_RUNTIME_ERROR_FMT("Variable {} = {} is not bisectable but Box::Bisect is called.", var, values_[i]);
141 }
142 switch (var.get_type()) {
144 return BisectContinuous(i);
147 return BisectInt(i);
149 DLINEAR_UNREACHABLE();
150 }
151 DLINEAR_UNREACHABLE();
152}
153
154std::pair<Box, Box> Box::Bisect(const Variable &var) const {
155 auto it = var_to_idx_->find(var);
156 if (it != var_to_idx_->end()) {
157 return Bisect(it->second);
158 } else {
159 DLINEAR_RUNTIME_ERROR_FMT("Variable {} is not found in this box.", var);
160 }
161 return Bisect((*var_to_idx_)[var]);
162}
163
164std::pair<Box, Box> Box::BisectInt(const int i) const {
165 DLINEAR_ASSERT(idx_to_var_->at(i).get_type() == Variable::Type::INTEGER ||
166 idx_to_var_->at(i).get_type() == Variable::Type::BINARY,
167 "Variable must be integer or binary");
168 const Interval &intv_i{values_[i]};
169 const mpz_class &lb{ceil(intv_i.lb())};
170 const mpz_class &ub{floor(intv_i.ub())};
171 const mpq_class &mid{intv_i.mid()};
172 const mpz_class &mid_floor{floor(mid)};
173 DLINEAR_ASSERT(intv_i.lb() <= lb, "intv_i lower bound must be less than or equal to lower bound");
174 DLINEAR_ASSERT(lb <= mid_floor, "lower bound must be less than or equal to mid_floor");
175 DLINEAR_ASSERT(mid_floor + 1 <= ub, "mid_floor + 1 must be less than or equal to upper bound");
176 DLINEAR_ASSERT(ub <= intv_i.ub(), "upper bound must be less than or equal to intv_i upper bound");
177
178 Box b1{*this};
179 Box b2{*this};
180 b1[i] = Interval(lb, mid_floor);
181 b2[i] = Interval(mid_floor + 1, ub);
182 return std::make_pair(b1, b2);
183}
184
185std::pair<Box, Box> Box::BisectContinuous(const int i) const {
186 DLINEAR_ASSERT(idx_to_var_->at(i).get_type() == Variable::Type::CONTINUOUS, "Variable must be continuous");
187 Box b1{*this};
188 Box b2{*this};
189 const Interval intv_i{values_[i]};
190 constexpr double kHalf{0.5};
191 const std::pair<Interval, Interval> bisected_intervals{intv_i.bisect(kHalf)};
192 b1[i] = bisected_intervals.first;
193 b2[i] = bisected_intervals.second;
194 return std::make_pair(b1, b2);
195}
196
197#if 0
198Box& Box::InplaceUnion(const Box& b) {
199 // Checks variables() == b.variables().
200 DLINEAR_ASSERT(equal(variables().begin(), variables().end(),
201 b.variables().begin(), b.variables().end(),
202 std::equal_to<Variable>{}));
203 values_ |= b.values_;
204 return *this;
205}
206#endif
207
208namespace {
209// RAII which preserves the FmtFlags of an ostream.
210class IosFmtFlagSaver {
211 public:
212 explicit IosFmtFlagSaver(std::ostream &os) : os_(os), flags_(os.flags()) {}
213 ~IosFmtFlagSaver() { os_.flags(flags_); }
214
215 IosFmtFlagSaver(const IosFmtFlagSaver &rhs) = delete;
216 IosFmtFlagSaver(IosFmtFlagSaver &&rhs) = delete;
217 IosFmtFlagSaver &operator=(const IosFmtFlagSaver &rhs) = delete;
218 IosFmtFlagSaver &operator=(IosFmtFlagSaver &&rhs) = delete;
219
220 private:
221 std::ostream &os_;
222 std::ios::fmtflags flags_;
223};
224} // namespace
225
226std::ostream &operator<<(std::ostream &os, const Box &box) {
227 IosFmtFlagSaver saver{os};
228 int i{0};
229 for (const Variable &var : *(box.variables_)) {
230 const Interval interval{box.values_[i++]};
231 os << var << " : ";
232 switch (var.get_type()) {
236 interval.PrintToStream(os, Infinity::ninfinity(box.lp_solver()), Infinity::infinity(box.lp_solver()));
237 break;
239 if (interval.ub() == 0.0)
240 os << "False";
241 else if (interval.lb() == 1.0)
242 os << "True";
243 else
244 os << "Unassigned";
245 break;
246 }
247 if (i != box.size()) os << "\n";
248 }
249 return os;
250}
251
252bool operator==(const Box &b1, const Box &b2) {
253 return std::equal(b1.variables().begin(), b1.variables().end(), b2.variables().begin(), b2.variables().end(),
254 std::equal_to<Variable>{}) &&
255 (b1.interval_vector() == b2.interval_vector());
256}
257
258bool operator!=(const Box &b1, const Box &b2) { return !(b1 == b2); }
259
260std::ostream &DisplayDiff(std::ostream &os, const std::vector<Variable> &variables, const std::vector<Interval> &old_iv,
261 const std::vector<Interval> &new_iv) {
262 IosFmtFlagSaver saver{os};
263 for (std::size_t i = 0; i < variables.size(); ++i) {
264 const Interval &old_i{old_iv[i]};
265 const Interval &new_i{new_iv[i]};
266 if (old_i != new_i) os << variables[i] << " : " << old_i << " -> " << new_i << "\n";
267 }
268 return os;
269}
270
271} // namespace dlinear
Collection of variables with associated intervals.
Definition Box.h:31
std::vector< Interval > values_
Interval vector of the box.
Definition Box.h:184
std::pair< mpq_class, int > MaxDiam() const
Return the max diameter of the box and the associated index.
Definition Box.cpp:124
std::shared_ptr< std::unordered_map< Variable, int > > var_to_idx_
Variable to index map.
Definition Box.h:186
std::vector< Interval > & m_interval_vector()
Return the interval vector of the box.
Definition Box.cpp:122
std::shared_ptr< std::vector< Variable > > variables_
Variables in the box.
Definition Box.h:185
void Add(const Variable &v)
Add the variable v to the box.
Definition Box.cpp:39
std::shared_ptr< std::unordered_map< int, Variable > > idx_to_var_
Index to variable map.
Definition Box.h:187
Interval & operator[](int i)
Return the interval at index i.
Definition Box.cpp:102
Config::LPSolver lp_solver_
LP solver.
Definition Box.h:183
std::pair< Box, Box > Bisect(int i) const
Bisect the box at i -th dimension.
Definition Box.cpp:137
int index(const Variable &var) const
Return the index associated with var.
Definition Box.cpp:119
std::pair< Box, Box > BisectInt(int i) const
Bisects the box at i -th dimension.
Definition Box.cpp:164
const std::vector< Variable > & variables() const
Get read-only access to the variables of the box.
Definition Box.cpp:113
Box(Config::LPSolver lp_solver)
Construct a new Box object associated with the given lp_solver.
Definition Box.cpp:26
const std::vector< Interval > & interval_vector() const
Return the interval vector of the box.
Definition Box.cpp:121
bool empty() const
Check whether the box is empty.
Definition Box.cpp:94
std::pair< Box, Box > BisectContinuous(int i) const
Bisects the box at i -th dimension.
Definition Box.cpp:185
int size() const
Get read-only access to the number of variables of the box.
Definition Box.cpp:100
const Variable & variable(int i) const
Return the i -th variable in the box.
Definition Box.cpp:115
void set_empty()
Clear the box, making it empty.
Definition Box.cpp:96
bool has_variable(const Variable &var) const
Checks if this box contains var.
Definition Box.cpp:117
LPSolver
Underlying LP solver used by the theory solver.
Definition Config.h:41
static const mpq_class & ninfinity(const Config &config)
Get the negative infinity value for the given LP solver in the config.
Definition Infinity.cpp:31
static const mpq_class & infinity(const Config &config)
Get the positive infinity value for the given LP solver in the config.
Definition Infinity.cpp:30
Represents a symbolic variable.
@ INTEGER
An INTEGER variable takes an int value.
@ BINARY
A BINARY variable takes an integer value from {0, 1}.
@ CONTINUOUS
A CONTINUOUS variable takes a mpq_class value.
@ BOOLEAN
A BOOLEAN variable takes a bool value.
Global namespace for the dlinear library.
bool IsInteger(const double v)
Returns true if v is represented by int.
Definition math.cpp:16
STL namespace.