SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
fmplex.cpp
Go to the documentation of this file.
1 #include "fmplex.h"
2 
3 namespace smtrat::fmplex {
4 
6  auto new_cols = parent.cols_to_elim;
7  new_cols.erase(std::find(new_cols.begin(), new_cols.end(), parent.chosen_col));
8 
9  std::size_t n_deleted_rows = parent.eliminators.size();
10  Matrix new_matr(parent.matrix.n_rows() - n_deleted_rows, parent.matrix.n_cols());
11  new_matr.reserve(parent.matrix.non_zeros_total() - 3*n_deleted_rows); // rough estimate.
12 
13  auto col_it = parent.eliminators.begin();
14  auto col_end = parent.eliminators.end();
15 
16  std::set<RowIndex> ignore;
17  auto ignore_it = parent.ignored.begin();
18 
19  for (RowIndex r = 0; r < parent.matrix.n_rows(); ++r) {
20  if (col_it != col_end && r == *col_it) ++col_it;
21  else {
22  new_matr.append_row(parent.matrix.row_begin(r), parent.matrix.row_end(r));
23  auto it = std::find(ignore_it, parent.ignored.end(), r);
24  if (it != parent.ignored.end()) {
25  ignore.emplace_hint(ignore.end(), new_matr.n_rows());
26  ignore_it = it;
27  ++ignore_it;
28  }
29  }
30  }
31 
32  parent.eliminators.clear();
33 
34  return Node(new_matr, new_cols, ignore);
35 }
36 
37 
39  assert(parent.type == Node::Type::LBS || parent.type == Node::Type::UBS);
40  assert(!parent.eliminators.empty());
41 
42  // remove chosen variable from elimination variables
43  auto new_cols = parent.cols_to_elim;
44  new_cols.erase(std::find(new_cols.begin(), new_cols.end(), parent.chosen_col));
45 
46  // set up new matrix
47  Matrix new_matr(parent.matrix.n_rows() - 1, parent.matrix.n_cols());
48  new_matr.reserve(parent.matrix.non_zeros_total()); // likely an overapproximation
49 
50  // eliminate using eliminator
51  RowIndex eliminator = parent.eliminators.back();
52  Rational elim_coeff = parent.matrix.coeff(eliminator, parent.chosen_col);
53  Rational elim_sgn = (parent.type == Node::Type::LBS ? Rational(1) : Rational(-1));
54  parent.eliminators.pop_back();
55 
56  auto col_it = parent.matrix.col_begin(parent.chosen_col);
57  auto col_end = parent.matrix.col_end(parent.chosen_col);
58 
59  std::set<RowIndex> ignore;
60  auto ignore_it = parent.ignored.begin();
61 
62  bool local_conflict = false; // TODO: make more elegant
63  bool inserted = false;
64 
65  auto process_row = [&](RowIndex r) {
66  inserted = false;
67  Rational scale_elim = elim_sgn*col_it->value;
68  Rational scale_r = carl::abs(elim_coeff);
69  auto combined_row = parent.matrix.combine(eliminator, scale_elim, r, scale_r);
70  gcd_normalize(combined_row);
71 
72  if (combined_row.front().col_index < m_first_parameter_col) {
73  // row still contains quantified variables
74  inserted = true;
75  new_matr.append_row(combined_row.begin(), combined_row.end());
76  return true;
77  }
78 
79  // all quantified variables are eliminated in this row
80  // add to overall result or analyze trivial constraint
81  if (is_trivial(combined_row)) {
82  if (is_conflict(combined_row)) {
83  if (is_positive_combination(combined_row)) return false;
84  else local_conflict = true;
85  }
86  } else if (is_positive_combination(combined_row)) {
87  collect_constraint(combined_row);
88  }
89 
90  return true;
91  };
92 
93  for (RowIndex r = 0; r < eliminator; ++r) {
94  if (r == col_it.row()) {
95  if (!process_row(r)) return Node::conflict();
96  ++col_it;
97  } else {
98  inserted = true;
99  new_matr.append_row(parent.matrix.row_begin(r), parent.matrix.row_end(r));
100  }
101  if (ignore_it != parent.ignored.end() && r == *ignore_it) {
102  if (inserted) ignore.insert(new_matr.n_rows() - 1);
103  ++ignore_it;
104  }
105  }
106  ++col_it;
107  for (RowIndex r = eliminator + 1; r < parent.matrix.n_rows(); ++r) {
108  if ((col_it != col_end) && (r == col_it.row())) {
109  if (!process_row(r)) return Node::conflict();
110  ++col_it;
111  } else {
112  inserted = true;
113  new_matr.append_row(parent.matrix.row_begin(r), parent.matrix.row_end(r));
114  }
115  if (ignore_it != parent.ignored.end() && r == *ignore_it) {
116  if (inserted) ignore.insert(new_matr.n_rows() - 1);
117  ++ignore_it;
118  }
119  }
120 
121  parent.ignored.insert(eliminator);
122 
123  if (local_conflict) return Node::leaf();
124  return Node(new_matr, new_cols, ignore);
125 }
126 
127 
129  parent.eliminators.clear();
130  std::vector<RowIndex> lbs, ubs;
131  // we can ignore non-bounds since they would have been added to the result at this point
132  auto col_it = parent.matrix.col_begin(parent.chosen_col);
133  auto col_end = parent.matrix.col_end(parent.chosen_col);
134  for (; col_it != col_end; ++col_it) {
135  if (col_it->value < 0) lbs.push_back(col_it.row());
136  else ubs.push_back(col_it.row());
137  }
138 
139  for (const RowIndex l : lbs) {
140  Rational coeff_l = (-1)*parent.matrix.coeff(l, parent.chosen_col);
141  for (const RowIndex u : ubs) {
142  Rational coeff_u = parent.matrix.coeff(u, parent.chosen_col);
143  auto combined_row = parent.matrix.combine(l, coeff_u, u, coeff_l);
144  gcd_normalize(combined_row);
145  if (is_trivial(combined_row)) {
146  if (is_global_conflict(combined_row)) return false;
147  } else if (is_positive_combination(combined_row)) {
148  collect_constraint(combined_row);
149  }
150  }
151  }
152  return true;
153 }
154 
155 
156 std::vector<Node> FMplexElimination::split_into_independent_nodes(const Node& n) const {
157  const Matrix& m = n.matrix;
158  std::vector<bool> col_used(n.cols_to_elim.size(), false);
159  std::vector<bool> row_used(m.n_rows(), false);
160  std::size_t n_unused_rows = m.n_rows();
161 
162  std::vector<std::size_t> pending;
163  std::vector<Node> result;
164 
165  for (std::size_t root_col = 0; root_col < n.cols_to_elim.size();) {
166  pending.push_back(root_col);
167  result.push_back(Node(Matrix(n_unused_rows, m.n_cols()), {}));
168  ++root_col;
169  while (!pending.empty()) {
170  std::size_t cur_col = pending.back();
171  pending.pop_back();
172 
173  if (col_used[cur_col]) continue;
174  col_used[cur_col] = true;
175 
176  ColIndex c = n.cols_to_elim[cur_col];
177  result.back().cols_to_elim.push_back(c);
178 
179  for (auto it = m.col_begin(c), col_end = m.col_end(c); it != col_end; ++it) {
180  if (row_used[it.row()]) continue;
181  for (const auto& e : m.row_entries(it.row())) {
182  if (e.col_index >= m_first_parameter_col) break;
183  if (e.col_index == c) continue;
184  for (std::size_t j = 0; ; ++j) {
185  assert(j < n.cols_to_elim.size());
186  if (n.cols_to_elim[j] == e.col_index) {
187  pending.push_back(j);
188  break;
189  }
190  }
191  }
192  row_used[it.row()] = true;
193  --n_unused_rows;
194  if (n.ignored.count(it.row()) > 0) {
195  result.back().ignored.insert(result.back().matrix.n_rows());
196  }
197  result.back().matrix.append_row(m.row_begin(it.row()), m.row_end(it.row()));
198  }
199  }
200  // find next column that is not yet covered
201  while (root_col < n.cols_to_elim.size() && col_used[root_col]) ++root_col;
202  }
203  for (Node& n : result) n.choose_elimination();
204  return result;
205 }
206 
207 }
bool is_trivial(const Row &row) const
Definition: fmplex.h:115
bool is_positive_combination(const Row &row) const
Definition: fmplex.h:127
std::vector< Node > split_into_independent_nodes(const Node &n) const
Definition: fmplex.cpp:156
Matrix::ColIndex ColIndex
Definition: fmplex.h:15
bool fm_elimination(Node &parent)
Definition: fmplex.cpp:128
void collect_constraint(const Row &row)
Definition: fmplex.h:140
bool is_global_conflict(const Row &row) const
Definition: fmplex.h:136
bool is_conflict(const Row &row) const
Definition: fmplex.h:120
Node bounded_elimination(Node &parent)
Definition: fmplex.cpp:38
Matrix::RowIndex RowIndex
Definition: fmplex.h:14
Node unbounded_elimination(Node &parent)
Definition: fmplex.cpp:5
Rational coeff(const RowIndex i, const ColIndex j) const
Definition: Matrix.h:76
col_iterator col_begin(ColIndex c) const
Definition: Matrix.h:194
col_iterator col_end(ColIndex c) const
Definition: Matrix.h:195
std::size_t non_zeros_total() const
Definition: Matrix.h:74
std::vector< RowEntry > combine(const RowIndex row_idx_1, const Rational &scale_1, const RowIndex row_idx_2, const Rational &scale_2) const
Computes scale_1*A_(i,-) + scale_2*A_(j,-) where i=row_idx_1, j=row_idx_2.
Definition: Matrix.h:106
std::size_t n_cols() const
Definition: Matrix.h:70
row_view row_entries(const RowIndex ri) const
Definition: Matrix.h:173
row_iterator row_end(RowIndex r) const
Definition: Matrix.h:161
void reserve(std::size_t n)
Definition: Matrix.h:67
row_iterator row_begin(RowIndex r) const
Definition: Matrix.h:160
RowIndex append_row(const It &begin, const It &end)
Appends the row contained in the range between begin and end to the matrix.
Definition: Matrix.h:91
std::size_t n_rows() const
Definition: Matrix.h:69
static bool find(V &ts, const T &t)
Definition: Alg.h:47
mpq_class Rational
Definition: Matrix.h:7
void gcd_normalize(std::vector< typename Matrix::RowEntry > &row)
Definition: Matrix.h:212
Numeric abs(const Numeric &_value)
Calculates the absolute value of the given Numeric.
Definition: Numeric.cpp:276
std::vector< ColIndex > cols_to_elim
Definition: Node.h:19
void choose_elimination()
Definition: Node.h:50
ColIndex chosen_col
Definition: Node.h:18
std::vector< RowIndex > eliminators
Definition: Node.h:20
std::set< RowIndex > ignored
Definition: Node.h:21
static Node leaf()
Definition: Node.h:48
static Node conflict()
Definition: Node.h:47
Matrix matrix
Definition: Node.h:16