dlinear  0.0.1
Delta-complete SMT solver for linear programming
Loading...
Searching...
No Matches
Driver.cpp
1
7#include "Driver.h"
8
9#include <iostream>
10
11#include "dlinear/util/exception.h"
12#include "dlinear/util/logging.h"
13
14namespace dlinear::mps {
15
16MpsDriver::MpsDriver(Context &context) : Driver{context, "MpsDriver"} {}
17
18bool MpsDriver::ParseStreamCore(std::istream &in) {
19 MpsScanner scanner(&in);
20 scanner.set_debug(debug_scanning_);
21 scanner_ = &scanner;
22
23 MpsParser parser(*this);
24 parser.set_debug_level(debug_parsing_);
25 const bool res = parser.parse() == 0;
26 scanner_ = nullptr;
27 return res;
28}
29
30bool MpsDriver::VerifyStrictBound(const std::string &bound) {
31 if (strict_mps_) {
32 if (bound_name_.empty()) {
33 bound_name_ = bound;
34 } else if (bound_name_ != bound) {
35 DLINEAR_WARN_FMT("First bound was '{}', found new bound '{}'. Skipping", bound_name_, bound);
36 return false;
37 }
38 }
39 return true;
40}
41
42bool MpsDriver::VerifyStrictRhs(const std::string &rhs) {
43 if (strict_mps_) {
44 if (rhs_name_.empty()) {
45 rhs_name_ = rhs;
46 } else if (rhs_name_ != rhs) {
47 DLINEAR_WARN_FMT("First RHS was '{}', found new RHS '{}'. Skipping", rhs_name_, rhs);
48 return false;
49 }
50 }
51 return true;
52}
53
54void MpsDriver::error(const location &l, const std::string &m) { std::cerr << l << " : " << m << std::endl; }
55
56void MpsDriver::ObjectiveSense(bool is_min) {
57 DLINEAR_TRACE_FMT("Driver::ObjectiveSense {}", is_min);
58 is_min_ = is_min;
59}
60
61void MpsDriver::ObjectiveName(const std::string &row) {
62 DLINEAR_TRACE_FMT("Driver::ObjectiveName {}", row);
63 obj_row_ = row;
64}
65
66void MpsDriver::AddRow(Sense sense, const std::string &row) {
67 DLINEAR_TRACE_FMT("Driver::AddRow {} {}", sense, row);
68 if (sense == Sense::N && obj_row_.empty()) {
69 DLINEAR_DEBUG("Objective row not found. Adding the first row with sense N as objective row");
70 obj_row_ = row;
71 }
72 row_senses_[row] = sense;
73}
74
75void MpsDriver::AddColumn(const std::string &column, const std::string &row, mpq_class value) {
76 DLINEAR_TRACE_FMT("Driver::AddColumn {} {} {}", row, column, value);
77 if (columns_.find(column) == columns_.end()) {
78 DLINEAR_TRACE_FMT("Added column {}", column);
79 // TODO(tend): choose if the name of the variable should be the column name or index
80 const Variable var{"x" + std::to_string(columns_.size())};
81 columns_[column] = var; // If not already in the map, add the variable
82 context_.DeclareVariable(var);
83 }
84 if (!context_.config().optimize() && row == obj_row_) return;
85 rows_[row].emplace(columns_[column], value);
86 DLINEAR_TRACE_FMT("Updated row {}", row);
87}
88
89void MpsDriver::AddRhs(const std::string &rhs, const std::string &row, mpq_class value) {
90 DLINEAR_TRACE_FMT("Driver::AddRhs {} {} {}", rhs, row, value);
91 if (!VerifyStrictRhs(rhs)) return;
92 rhs_values_[row] = value;
93 Expression row_expression = ExpressionAddFactory{0, rows_[row]}.GetExpression();
94 try {
95 switch (row_senses_.at(row)) {
96 case Sense::L:
97 rhs_[row] = row_expression <= value;
98 break;
99 case Sense::G:
100 rhs_[row] = value <= row_expression;
101 break;
102 case Sense::E:
103 rhs_[row] = row_expression == value;
104 break;
105 case Sense::N:
106 DLINEAR_WARN("Sense N is used only for objective function. No action to take");
107 break;
108 default:
109 DLINEAR_UNREACHABLE();
110 }
111 } catch (const std::out_of_range &e) {
112 DLINEAR_RUNTIME_ERROR_FMT("Row {} not found", row);
113 }
114 DLINEAR_TRACE_FMT("Updated rhs {}", rhs_[row]);
115}
116
117void MpsDriver::AddRange(const std::string &rhs, const std::string &row, mpq_class value) {
118 DLINEAR_TRACE_FMT("Driver::AddRange {} {} {}", rhs, row, value);
119 if (!VerifyStrictRhs(rhs)) return;
120 try {
121 Expression row_expression = ExpressionAddFactory{0, rows_[row]}.GetExpression();
122 switch (row_senses_.at(row)) {
123 case Sense::L:
124 mpq_abs(value.get_mpq_t(), value.get_mpq_t());
125 rhs_[row] &= mpq_class{rhs_values_[row] - value} <= row_expression;
126 break;
127 case Sense::G:
128 mpq_abs(value.get_mpq_t(), value.get_mpq_t());
129 rhs_[row] &= row_expression <= mpq_class{rhs_values_[row] + value};
130 break;
131 case Sense::E:
132 rhs_[row] = value > 0
133 ? rhs_values_[row] <= row_expression && row_expression <= mpq_class(rhs_values_[row] + value)
134 : mpq_class{rhs_values_[row] + value} <= row_expression && row_expression <= rhs_values_[row];
135 break;
136 case Sense::N:
137 DLINEAR_WARN("Sense N is used only for objective function. No action to take");
138 break;
139 default:
140 DLINEAR_UNREACHABLE();
141 }
142 } catch (const std::out_of_range &e) {
143 DLINEAR_RUNTIME_ERROR_FMT("Row {} not found", row);
144 }
145}
146
147void MpsDriver::AddBound(BoundType bound_type, const std::string &bound, const std::string &column, mpq_class value) {
148 DLINEAR_TRACE_FMT("Driver::AddBound {} {} {} {}", bound_type, bound, column, value);
149 if (!VerifyStrictBound(bound)) return;
150 try {
151 switch (bound_type) {
152 case BoundType::UP:
153 case BoundType::UI:
154 bounds_[column] &= columns_.at(column) <= value;
155 if (value <= 0) skip_lower_bound_[column] = true;
156 break;
157 case BoundType::LO:
158 case BoundType::LI:
159 bounds_[column] &= value <= columns_.at(column);
160 skip_lower_bound_[column] = true;
161 break;
162 case BoundType::FX:
163 bounds_[column] &= (value <= columns_.at(column)) && (columns_.at(column) <= value);
164 skip_lower_bound_[column] = true;
165 break;
166 default:
167 DLINEAR_UNREACHABLE();
168 }
169 } catch (const std::out_of_range &e) {
170 DLINEAR_RUNTIME_ERROR_FMT("Column {} not found", column);
171 }
172
173 DLINEAR_TRACE_FMT("Updated bound {}", bounds_[column]);
174}
175
176void MpsDriver::AddBound(BoundType bound_type, const std::string &bound, const std::string &column) {
177 DLINEAR_TRACE_FMT("Driver::AddBound {} {} {}", bound_type, bound, column);
178 if (!VerifyStrictBound(bound)) return;
179 try {
180 switch (bound_type) {
181 case BoundType::BV:
182 bounds_[column] = (0 <= columns_.at(column)) && (columns_.at(column) <= 1);
183 skip_lower_bound_[column] = true;
184 break;
185 case BoundType::FR:
186 case BoundType::MI:
187 skip_lower_bound_[column] = true;
188 break;
189 case BoundType::PL:
190 DLINEAR_DEBUG("Infinity bound, no action to take");
191 break;
192 default:
193 DLINEAR_UNREACHABLE();
194 }
195 } catch (const std::out_of_range &e) {
196 DLINEAR_RUNTIME_ERROR_FMT("Column {} not found", column);
197 }
198
199 DLINEAR_TRACE_FMT("Updated bound {}", bounds_[column]);
200}
201
202void MpsDriver::End() {
203 DLINEAR_DEBUG_FMT("Driver::EndData reached end of file {}", problem_name_);
204 for (const auto &[row, sense] : row_senses_) {
205 if (sense != Sense::N && rhs_.find(row) == rhs_.end()) {
206 DLINEAR_TRACE_FMT("Row {} has no RHS. Adding 0", row);
207 AddRhs(rhs_name_, row, 0);
208 }
209 }
210 for (const auto &[column, var] : columns_) {
211 if (skip_lower_bound_.find(column) == skip_lower_bound_.end()) {
212 DLINEAR_TRACE_FMT("Column has no lower bound. Adding 0 <= {}", column);
213 AddBound(BoundType::LO, bound_name_, column, 0);
214 }
215 }
216 DLINEAR_DEBUG_FMT("Found {} assertions", n_assertions());
217 for (const auto &[name, bound] : bounds_) {
218 context_.Assert(bound);
219 }
220 for (const auto &[name, row] : rhs_) {
221 if (row.EqualTo(Formula::True())) continue;
222 context_.Assert(row);
223 }
224 if (context_.config().optimize() && !obj_row_.empty()) {
225 Expression obj_expression = ExpressionAddFactory{0, rows_.at(obj_row_)}.GetExpression();
226 if (is_min_) {
227 context_.Minimize(obj_expression);
228 } else {
229 context_.Maximize(obj_expression);
230 }
231 }
232}
233
234void MpsDriver::ToSmt2(std::ostream &os) const {
235 os << "(set-logic QF_LRA)\n";
236 if (!context_.GetInfo(":status").empty()) os << "(set-info :status " << context_.GetInfo(":status") << ")\n";
237 for (const auto &[name, column] : columns_) {
238 os << "(declare-const " << column << " Real)\n";
239 }
240 for (const auto &[name, bound] : bounds_) {
241 os << "(assert " << bound.to_smt2_string() << ")\n";
242 }
243 for (const auto &[name, row] : rhs_) {
244 if (row.EqualTo(Formula::True())) continue;
245 os << "(assert " << row.to_smt2_string() << ")\n";
246 }
247 os << "(check-sat)\n";
248}
249
250} // namespace dlinear::mps
Factory class to help build ExpressionAdd expressions.
Expression GetExpression()
Returns a symbolic expression.
Represents a symbolic form of an expression.
Represents a symbolic variable.
MpsScanner is a derived class to add some extra function to the scanner class.
Definition scanner.h:40
void set_debug(bool b)
Enable debug output (via arg_yyout) if compiled into the scanner.
Namespace for the MPS parser of the dlinear library.
Definition BoundType.cpp:13
Sense
Sense of a constraint row.
Definition Sense.h:23
BoundType
Bound type.
Definition BoundType.h:27