SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
eliminate.cpp
Go to the documentation of this file.
1 #include "eliminate.h"
2 #include <iostream>
3 #include <fstream>
4 
5 
6 namespace smtrat::fmplex {
7 
8 template<typename Var>
9 struct VariableIndex {
10  std::vector<Var> m_vars;
11 
13 
14  explicit VariableIndex(const std::vector<Var>& vs) : m_vars(vs) {}
15 
16  std::size_t add_variable(const Var v) {
17  if (std::find(m_vars.begin(), m_vars.end(), v) == m_vars.end()) {
18  m_vars.push_back(v);
19  }
20  return m_vars.size() - 1;
21  }
22 
23  std::size_t index(Var v) const {
24  auto it = std::find(m_vars.begin(), m_vars.end(), v);
25  assert(it != m_vars.end());
26  return std::distance(m_vars.begin(), it);
27  }
28 
29  Var var(std::size_t i) const {
30  assert(i < m_vars.size());
31  return m_vars[i];
32  }
33 
34  std::size_t size() const { return m_vars.size(); }
35 };
36 
37 
38 using Poly = carl::MultivariatePolynomial<Rational>;
39 
40 
41 Formula eliminate_variables(const Formula& f, const std::vector<carl::Variable>& vars) {
42  if (f.type() == carl::FormulaType::CONSTRAINT) {
43  auto f_vars = f.variables();
44  if (std::any_of(
45  vars.begin(), vars.end(), [&f_vars](const auto& v){ return f_vars.count(v) > 0; }
46  )) {
47  return Formula(carl::FormulaType::TRUE);
48  }
49  return f;
50  }
51  assert(f.type() == carl::FormulaType::AND);
52 
53  // transform formula to matrix
54  std::vector<Formula> constraints = f.subformulas();
55  // TODO: equality substitution, filtering
57  carl::carlVariables vs;
58  for (const auto& f : constraints) carl::variables(f, vs);
59  for (const auto v : vs) var_idx.add_variable(v);
60 
61  // reserve enough space for the matrix
62  std::size_t constant_col = var_idx.size();
63  std::size_t delta_col = constant_col + 1;
64  Matrix m(constraints.size(), var_idx.size() + constraints.size() + 2); // +2 : delta & rhs.
65  std::size_t non_zeros = 2*constraints.size(); // one origin & at most one delta for each row
66  for (const auto& f : constraints) non_zeros += f.constraint().lhs().nr_terms();
67  m.reserve(non_zeros);
68 
69  // transform each constraint into a row
70  for (std::size_t r = 0; r < constraints.size(); ++r) {
71  carl::Relation rel = constraints[r].constraint().relation();
72 
73  // smtrat automatically converts constraints to < or <=
74  assert(rel != carl::Relation::GREATER && rel != carl::Relation::GEQ);
75  assert(rel != carl::Relation::NEQ); // we don't support NEQ yet
76 
77  Poly lhs = constraints[r].constraint().lhs();
78  Rational constant_part = lhs.constant_part();
79  lhs -= constant_part;
80 
81  std::vector<Matrix::RowEntry> entries; // TODO: make it so that the contents of the row are actually already in the matrix data
82  for (const auto& term : lhs) {
83  entries.emplace_back(var_idx.index(term.single_variable()), term.coeff());
84  }
85  // the order in the polynomial may be different from the order in the var index
86  std::sort(entries.begin(), entries.end(),
87  [](const auto& lhs, const auto& rhs){ return lhs.col_index < rhs.col_index; }
88  );
89 
90  // constant part, delta, and origin
91  if (!carl::is_zero(constant_part)) entries.emplace_back(constant_col, constant_part);
92  if (rel == carl::Relation::LESS) entries.emplace_back(delta_col, Rational(1));
93  entries.emplace_back(delta_col + r + 1, Rational(1));
94 
95  m.append_row(entries.begin(), entries.end());
96  }
97 
98  Matrix res = FMplexElimination(m, vars.size(), var_idx.size() - vars.size()).apply();
99 
100  // transform Matrix back to formula
101  if (res.n_rows() == 0) return Formula(carl::FormulaType::TRUE);
102  std::vector<Formula> conjuncts;
103  for (std::size_t i = 0; i < res.n_rows(); ++i) {
104  Poly lhs;
105  auto it = res.row_begin(i);
106  auto row_end = res.row_end(i);
107  for (; it !=row_end && it->col_index < constant_col; ++it) {
108  lhs += it->value*Poly(var_idx.var(it->col_index));
109  }
110  if (it != row_end && it->col_index == constant_col) {
111  lhs += it->value;
112  ++it;
113  }
114  // This method is only applied to pos.lin. combinations, so the delta coeff will be >=0
115  if (it != row_end && it->col_index == delta_col) conjuncts.emplace_back(lhs, carl::Relation::LESS);
116  else conjuncts.emplace_back(lhs, carl::Relation::LEQ);
117  }
118 
119  if (conjuncts.size() == 1) return conjuncts.front();
120  else return Formula(carl::FormulaType::AND, conjuncts);
121 }
122 
123 
124 
125 std::pair<EigenMat, EigenVec> eliminate_cols(const EigenMat& constraints,
126  const EigenVec& constants,
127  const std::vector<std::size_t>& cols) {
128  // convert to internal matrix type
129  Matrix m(constraints.rows(), constraints.cols() + 2 + constraints.rows());
130  std::size_t quantified_cols = cols.size();
131  VariableIndex<std::size_t> var_idx(cols);
132  for (std::size_t i = 0, q = 0; i < constraints.rows(); ++i) {
133  if (q < cols.size() && i == cols[q]) ++q;
134  else var_idx.add_variable(i);
135  }
136 
137  for (std::size_t i = 0; i < constraints.rows(); ++i) {
138  std::vector<Matrix::RowEntry> row;
139  for (std::size_t j = 0; j < constraints.cols(); ++j) {
140  if (!carl::is_zero(constraints(i,var_idx.var(j)))) {
141  row.emplace_back(j, constraints(i,var_idx.var(j)));
142  }
143  }
144  if (!carl::is_zero(constants(i))) row.emplace_back(constraints.cols(), -constants(i));
145  row.emplace_back(constraints.cols() + 1 + i, Rational(1)); // TODO: maybe this transformation should be done by fmplex internally?
146  m.append_row(row.begin(), row.end());
147  }
148 
149  Matrix res = FMplexElimination(m, quantified_cols, constraints.cols() - quantified_cols).apply();
150 
151  EigenMat res_mat = EigenMat::Zero(res.n_rows(), constraints.cols());
152  EigenVec res_const = EigenVec::Zero(res.n_rows());
153  // convert back to original format
154  for (std::size_t i = 0; i < res.n_rows(); ++i) {
155  for (const auto& e : res.row_entries(i)) {
156  if (e.col_index == constraints.cols()) {
157  res_const(i) = -e.value;
158  break;
159  }
160  if (e.col_index > constraints.cols()) break;
161  res_mat(i, var_idx.var(e.col_index)) = e.value;
162  }
163  }
164 
165  return {res_mat, res_const};
166 }
167 
168 
169 void write_matrix_to_ine(const EigenMat& constraints,
170  const EigenVec& constants,
171  const std::vector<std::size_t>& cols,
172  const std::string& filename) {
173  std::ofstream file;
174  file.open(filename);
175  file << "H-representation\n";
176  file << "begin\n";
177  file << constraints.rows() << " " << constraints.cols() + 1 << " rational\n";
178  for (std::size_t i = 0; i < constraints.rows(); ++i) {
179  Rational lcm = (carl::is_zero(constants(i)) ? Rational(1) : constants(i).get_den());
180  for (std::size_t j = 0; j < constraints.cols(); ++j) {
181  lcm = carl::lcm(lcm.get_num(), constraints(i,j).get_den());
182  }
183  file << " " << constants(i)*lcm; // first column contains the constants
184  for (std::size_t j = 0; j < constraints.cols(); ++j) {
185  file << " " << constraints(i,j)*(-lcm);
186  }
187  file << "\n";
188  }
189  file << "end\n";
190  file << "project " << cols.size();
191  for (const auto i : cols) file << " " << i+1;
192  file << "\n";
193  file.close();
194 }
195 
196 }
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
void sort(T *array, int size, LessThan lt)
Definition: Sort.h:67
int Var
Definition: SolverTypes.h:42
static bool find(V &ts, const T &t)
Definition: Alg.h:47
Eigen::Matrix< Rational, Eigen::Dynamic, Eigen::Dynamic > EigenMat
Definition: eliminate.h:17
void write_matrix_to_ine(const EigenMat &constraints, const EigenVec &constants, const std::vector< std::size_t > &cols, const std::string &filename)
Definition: eliminate.cpp:169
std::pair< EigenMat, EigenVec > eliminate_cols(const EigenMat &constraints, const EigenVec &constants, const std::vector< std::size_t > &cols)
Definition: eliminate.cpp:125
carl::MultivariatePolynomial< Rational > Poly
Definition: eliminate.cpp:38
Eigen::Matrix< Rational, Eigen::Dynamic, 1 > EigenVec
Definition: eliminate.h:18
mpq_class Rational
Definition: Matrix.h:7
Formula eliminate_variables(const Formula &f, const std::vector< carl::Variable > &vars)
Definition: eliminate.cpp:41
carl::Formula< carl::MultivariatePolynomial< Rational > > Formula
Definition: eliminate.h:16
Numeric lcm(const Numeric &_valueA, const Numeric &_valueB)
Calculates the least common multiple of the two arguments.
Definition: Numeric.cpp:302
std::vector< carl::Variable > vars(const std::pair< QuantifierType, std::vector< carl::Variable >> &p)
Definition: QEQuery.h:43
std::size_t add_variable(const Var v)
Definition: eliminate.cpp:16
std::size_t index(Var v) const
Definition: eliminate.cpp:23
Var var(std::size_t i) const
Definition: eliminate.cpp:29
VariableIndex(const std::vector< Var > &vs)
Definition: eliminate.cpp:14
std::vector< Var > m_vars
Definition: eliminate.cpp:10
std::size_t size() const
Definition: eliminate.cpp:34