SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
Subtropical.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <carl-arith/constraint/BasicConstraint.h>
4 #include <boost/container/flat_map.hpp>
5 
6 /**
7  * Implements data structures and encodings for the subtropical method.
8  *
9  *
10  */
12 
13 enum class SeparatorType { STRICT = 0,
14  WEAK = 1,
15  SEMIWEAK = 2 };
16 
17 /**
18  * Represents the normal vector component and the sign variable
19  * assigned to a variable in an original constraint.
20  */
21 struct Moment {
22  /// Normal vector component of the separating hyperplane
23  const carl::Variable normal_vector;
24  /// Boolean variable representing the sign variant
25  const carl::Variable sign_variant;
26 
28  : normal_vector(carl::fresh_real_variable()), sign_variant(carl::fresh_boolean_variable()) {}
29 };
30 
31 /**
32  * Represents a term of an original constraint and assigns
33  * him a rating variable if a weak separator is searched.
34  */
35 struct Vertex {
36  /// Coefficient of the assigned term
38  /// Monomial of the assigned term
39  const carl::Monomial::Arg monomial;
40  /// Rating variable of the term for a weak separator
41  mutable carl::Variable m_rating;
42 
43  Vertex(const TermT& term)
44  : coefficient(term.coeff()), monomial(term.monomial()), m_rating(carl::Variable::NO_VARIABLE) {}
45 
46  carl::Variable rating() const {
47  if (m_rating == carl::Variable::NO_VARIABLE) m_rating = carl::fresh_real_variable();
48  return m_rating;
49  }
50 };
51 
52 /// Subdivides the relations into classes with the same linearization result
53 enum class Direction { BOTH = 0,
54  NEGATIVE = 1,
55  POSITIVE = 2 };
56 
57 inline carl::BasicConstraint<Poly> normalize(const carl::BasicConstraint<Poly>& c) {
58  assert(c.relation() == carl::Relation::LESS || c.relation() == carl::Relation::LEQ || c.relation() == carl::Relation::EQ || c.relation() == carl::Relation::NEQ);
59  return carl::BasicConstraint<Poly>(c.lhs().normalize(), carl::is_negative(c.lhs().lcoeff()) ? carl::turn_around(c.relation()) : c.relation());
60 }
61 
62 inline std::optional<Direction> direction(carl::Relation r) {
63  switch (r) {
64  case carl::Relation::GREATER:
65  case carl::Relation::GEQ:
66  return Direction::POSITIVE;
67  break;
68  case carl::Relation::LESS:
69  case carl::Relation::LEQ:
70  return Direction::NEGATIVE;
71  break;
72  case carl::Relation::NEQ:
73  return Direction::BOTH;
74  break;
75  default:
76  return std::nullopt;
77  }
78 }
79 
80 /**
81  * Represents the class of all original constraints with the same
82  * left hand side after a normalization. Here, the set of all received
83  * relations of constraints with the same left hand side is stored. At any
84  * one time only one relation can be active and used for linearization.
85  */
86 struct Separator {
87  /// Bias variable of the separating hyperplane
88  const carl::Variable bias;
89  /// Vertices for all terms of the normalized left hand side
90  const std::vector<Vertex> vertices;
91 
92  Separator(const Poly& normalization)
93  : bias(carl::fresh_real_variable()), vertices(normalization.begin(), normalization.end()) {}
94 };
95 
96 inline std::optional<Direction> direction_for_equality(const Separator& s) {
97  // compute point (1,...,1) at polynomial
98  Rational sum_of_coeffs = 0;
99  for(const auto& v : s.vertices){
100  sum_of_coeffs += v.coefficient;
101  }
102  if (sum_of_coeffs == 0) return std::nullopt; // root found
103  else return (sum_of_coeffs < 0) ? subtropical::Direction::POSITIVE : subtropical::Direction::NEGATIVE;
104 }
105 
106 class Encoding {
107  /// Maps a variable to the components of the moment function
108  std::unordered_map<carl::Variable, Moment> m_moments;
109 
110 public:
111  /**
112  * Creates the formula for the hyperplane that linearly separates at least one
113  * variant positive frame vertex from all variant negative frame vertices. If a
114  * weak separator is searched, the corresponding rating is included.
115  * @param separator The separator object that stores the construction information.
116  * @param negated True, if the formula for the negated polynomial shall be constructed.
117  * False, if the formula for the original polynomial shall be constructed.
118  * @return Formula that is satisfiable iff such a separating hyperplane exists.
119  */
120  inline FormulaT encode_separator(const Separator& separator, const Direction direction, const SeparatorType separator_type) {
121  if (direction == Direction::BOTH) {
123  encode_separator(separator, Direction::POSITIVE, separator_type),
124  encode_separator(separator, Direction::NEGATIVE, separator_type));
125  } else {
127  bool negated = (direction == Direction::NEGATIVE);
128  Poly totalRating;
129  FormulasT disjunctions, conjunctions;
130  for (const Vertex& vertex : separator.vertices) {
131  /// Create the hyperplane and sign change formula
132  Poly hyperplane{separator.bias};
133  FormulaT signChangeFormula;
134  if (vertex.monomial) {
135  FormulasT signChangeSubformulas;
136  for (const auto& exponent : vertex.monomial->exponents()) {
137  const auto& moment{m_moments[exponent.first]};
138  hyperplane += carl::from_int<Rational>(exponent.second) * moment.normal_vector;
139  if (exponent.second % 2 == 1)
140  signChangeSubformulas.emplace_back(moment.sign_variant);
141  }
142  signChangeFormula = FormulaT(carl::FormulaType::XOR, std::move(signChangeSubformulas));
143  }
144 
145  /// Create the rating case distinction formula
146  if (separator_type == SeparatorType::WEAK) {
147  totalRating += vertex.rating();
148  conjunctions.emplace_back(
149  carl::FormulaType::IMPLIES,
150  FormulaT(hyperplane, carl::Relation::LESS),
151  FormulaT(Poly(vertex.rating()), carl::Relation::EQ));
152  const Rational coefficient{negated ? -vertex.coefficient : vertex.coefficient};
153  conjunctions.emplace_back(
154  carl::FormulaType::IMPLIES,
155  FormulaT(hyperplane, carl::Relation::EQ),
156  FormulaT(
158  FormulaT(
159  carl::FormulaType::IMPLIES,
160  signChangeFormula,
161  FormulaT(vertex.rating() + coefficient, carl::Relation::EQ)),
162  FormulaT(
163  carl::FormulaType::IMPLIES,
164  signChangeFormula.negated(),
165  FormulaT(vertex.rating() - coefficient, carl::Relation::EQ))));
166  }
167 
168  /// Create the strict/weak linear separating hyperplane
169  bool positive{carl::is_positive(vertex.coefficient) != negated};
170  disjunctions.emplace_back(
171  FormulaT(
172  carl::FormulaType::IMPLIES,
173  positive ? signChangeFormula.negated() : signChangeFormula,
174  FormulaT(hyperplane, separator_type == SeparatorType::STRICT || separator_type == SeparatorType::SEMIWEAK ? carl::Relation::LEQ : carl::Relation::LESS))
175  .negated());
176  conjunctions.emplace_back(
177  carl::FormulaType::IMPLIES,
178  positive ? std::move(signChangeFormula) : std::move(signChangeFormula.negated()),
179  FormulaT(std::move(hyperplane), separator_type == SeparatorType::STRICT ? carl::Relation::LESS : carl::Relation::LEQ));
180  }
181  if (separator_type == SeparatorType::WEAK)
182  conjunctions.emplace_back(totalRating, carl::Relation::GREATER);
183  return FormulaT(
185  FormulaT(carl::FormulaType::OR, std::move(disjunctions)),
186  FormulaT(carl::FormulaType::AND, std::move(conjunctions)));
187  }
188  }
189 
190  inline FormulaT encode_integer(carl::Variable var) {
191  auto& moment = m_moments.at(var);
192  return FormulaT(Poly(moment.normal_vector), carl::Relation::GEQ);
193  }
194 
195  inline Model construct_assignment(const Model& lra_model, const FormulaT& f) const {
196  /// Stores all informations retrieved from the LRA solver to construct the model
197  struct Weight {
198  const carl::Variable& variable;
199  Rational exponent;
200  const bool sign;
201  Weight(const carl::Variable& var, const Rational& exp, const bool sgn)
202  : variable(var), exponent(exp), sign(sgn) {}
203  };
204  std::vector<Weight> weights;
205 
206  auto used_vars = carl::variables(f);
207 
208  // Retrieve the sign and exponent for every active variable
209  Rational gcd(0);
210  for (const auto& momentsEntry : m_moments) {
211  const carl::Variable& variable{momentsEntry.first};
212  const Moment& moment{momentsEntry.second};
213  if (used_vars.has(variable)) {
214  auto signIter{lra_model.find(moment.sign_variant)};
215  weights.emplace_back(variable, lra_model.at(moment.normal_vector).asRational(), signIter != lra_model.end() && signIter->second.asBool());
216  carl::gcd_assign(gcd, weights.back().exponent);
217  }
218  }
219 
220  // get assignment for (Boolean) variables from original formula
221  Model lra_model_nonenc;
222  for (const auto& v : used_vars) {
223  if (lra_model.find(v) != lra_model.end()) {
224  lra_model_nonenc.emplace(v, lra_model.at(v));
225  }
226  }
227 
228  // Calculate smallest possible integer valued exponents
229  if (!carl::is_zero(gcd) && !carl::is_one(gcd))
230  for (Weight& weight : weights)
231  weight.exponent /= gcd;
232 
233  // Find model by increasingly testing the sample base
234  Model result;
235  Rational base = 0;
236  do {
237  result = lra_model_nonenc;
238  ++base;
239  for (const Weight& weight : weights) {
240  Rational value{carl::is_negative(weight.exponent) ? carl::reciprocal(base) : base};
241  carl::pow_assign(value, carl::to_int<carl::uint>(carl::abs(weight.exponent)));
242  if (weight.sign)
243  value *= -1;
244  result.emplace(weight.variable, value);
245  }
246  } while (!satisfied_by(f, result));
247  return result;
248  }
249 };
250 
251 /**
252  * Requires a quantifier-free real arithmetic formula with no negations
253  *
254  * @param formula The formula to transform
255  *
256  * @return an equisatisfiable equation
257  */
258 inline FormulaT transform_to_equation(const FormulaT& formula) {
259  if (formula.type() == carl::FormulaType::TRUE || formula.type() == carl::FormulaType::FALSE) {
260  return formula;
261  } else if (formula.type() == carl::FormulaType::CONSTRAINT) {
262  carl::Relation relation = formula.constraint().relation();
263  Poly polynomial = formula.constraint().lhs();
264  if (relation == carl::Relation::EQ) {
265  return formula;
266  }
267  Poly transformedPolynomial{polynomial};
268  Poly parameter{carl::fresh_real_variable()};
269  switch (relation) {
270  case carl::Relation::GREATER:
271  transformedPolynomial *= (parameter *= parameter);
272  transformedPolynomial -= Poly{Rational(1)};
273  break;
274  case carl::Relation::GEQ:
275  transformedPolynomial -= (parameter *= parameter);
276  break;
277  case carl::Relation::LESS:
278  transformedPolynomial *= (parameter *= parameter);
279  transformedPolynomial += Poly{Rational(1)};
280  break;
281  case carl::Relation::LEQ:
282  transformedPolynomial += (parameter *= parameter);
283  break;
284  case carl::Relation::NEQ:
285  transformedPolynomial *= parameter;
286  transformedPolynomial += Poly{Rational(1)};
287  break;
288  default:
289  assert(false);
290  return FormulaT();
291  }
292  return FormulaT(transformedPolynomial, carl::Relation::EQ);
293  } else if (formula.type() == carl::FormulaType::OR) {
294  Poly product{Rational(1)};
295  for (const auto& subformula : formula.subformulas()) {
296  FormulaT transformed = transform_to_equation(subformula);
297  if (transformed.type() == carl::FormulaType::TRUE) {
298  return transformed;
299  } else if (transformed.type() != carl::FormulaType::FALSE) {
300  product *= transformed.constraint().lhs();
301  }
302  }
303  return FormulaT(product, carl::Relation::EQ);
304  } else if (formula.type() == carl::FormulaType::AND) {
305  return FormulaT(carl::FormulaType::FALSE);
306  } else {
307  assert(false);
308  return FormulaT(carl::FormulaType::FALSE);
309  }
310 }
311 
312 /**
313  * Requires a quantifier-free real arithmetic formula with no negations
314  *
315  * @param formula The formula to translate
316  *
317  * @return linear formula
318  */
319 inline FormulaT encode_as_formula(const FormulaT& formula, Encoding& encoding, SeparatorType separator_type) {
320  if (formula.type() == carl::FormulaType::TRUE || formula.type() == carl::FormulaType::FALSE) {
321  return formula;
322  } else if (formula.type() == carl::FormulaType::BOOL) {
323  return formula;
324  } else if (formula.type() == carl::FormulaType::NOT) {
325  assert(formula.subformula().type() == carl::FormulaType::BOOL);
326  return formula;
327  } else if (formula.type() == carl::FormulaType::CONSTRAINT) {
328  if (formula.constraint().relation() == carl::Relation::EQ) {
329  return FormulaT(carl::FormulaType::FALSE);
330  }
331  auto constr = normalize(formula.constraint().constr());
332  Separator separator(constr.lhs());
333  Direction dir = *direction(constr.relation());
334  return encoding.encode_separator(separator, dir, separator_type);
335  } else if (formula.type() == carl::FormulaType::OR) {
336  FormulasT disjunctions;
337  for (const auto& subformula : formula.subformulas()) {
338  disjunctions.push_back(encode_as_formula(subformula, encoding, separator_type));
339  }
340  return FormulaT(carl::FormulaType::OR, std::move(disjunctions));
341  } else if (formula.type() == carl::FormulaType::AND) {
342  FormulasT conjunctions;
343  for (const auto& subformula : formula.subformulas()) {
344  conjunctions.push_back(encode_as_formula(subformula, encoding, separator_type));
345  }
346  return FormulaT(carl::FormulaType::AND, std::move(conjunctions));
347  } else {
348  assert(false);
349  return FormulaT(carl::FormulaType::FALSE);
350  }
351 }
352 
353 /**
354  * Requires a quantifier-free real arithmetic formula with no negations
355  *
356  * @param formula The formula to translate
357  *
358  * @return linear formula
359  */
360 inline FormulaT encode_as_formula_alt(const FormulaT& formula, Encoding& encoding, SeparatorType separator_type) {
361  std::vector<FormulaT> constraints;
362  carl::arithmetic_constraints(formula, constraints);
363  std::set<FormulaT> constraint_set(constraints.begin(), constraints.end());
364  auto res_boolean = formula;
365  std::map<Poly, boost::container::flat_map<carl::Relation, std::pair<FormulaT, FormulaT>>> encodings;
366  for (const auto& c : constraint_set) {
367  if (c.constraint().relation() == carl::Relation::EQ) {
368  res_boolean = carl::substitute(res_boolean, c, FormulaT(carl::FormulaType::FALSE));
369  } else {
370  auto constr = normalize(c.constraint().constr());
371  Separator separator(constr.lhs());
372  Direction dir = *direction(constr.relation());
373  auto& map = encodings.try_emplace(constr.lhs()).first->second;
374  assert(map.find(constr.relation()) == map.end());
375  FormulaT var = map.find(carl::inverse(constr.relation())) == map.end() ? FormulaT(carl::fresh_boolean_variable()) : map.at(carl::inverse(constr.relation())).first.negated();
376  map.emplace(constr.relation(), std::make_pair(var, encoding.encode_separator(separator, dir, separator_type)));
377  res_boolean = carl::substitute(res_boolean, c, var);
378  }
379  }
380  std::vector<FormulaT> res({res_boolean});
381  for (auto& poly : encodings) {
382  for (auto rel = poly.second.begin(); rel != poly.second.end(); ) {
383  auto& enc = rel->second;
384  bool relevant = false;
385  carl::visit(res_boolean, [&enc, &relevant](const FormulaT& f) { if (f==enc.first) relevant = true; } );
386  if (relevant) {
387  res.emplace_back(carl::FormulaType::IMPLIES, enc.first, enc.second);
388  //res.emplace_back(carl::FormulaType::IFF, enc.first, enc.second);
389  rel++;
390  } else {
391  rel = poly.second.erase(rel);
392  }
393  }
394  if (poly.second.find(carl::Relation::LESS) != poly.second.end() && poly.second.find(carl::Relation::GREATER) != poly.second.end()) {
395  res.emplace_back(carl::FormulaType::OR, poly.second.at(carl::Relation::LESS).first.negated(), poly.second.at(carl::Relation::GREATER).first.negated());
396  }
397  if (poly.second.find(carl::Relation::LEQ) != poly.second.end() && poly.second.find(carl::Relation::GEQ) != poly.second.end()) {
398  res.emplace_back(carl::FormulaType::OR, poly.second.at(carl::Relation::LEQ).first.negated(), poly.second.at(carl::Relation::GEQ).first.negated());
399  }
400  }
401  return FormulaT(carl::FormulaType::AND, std::move(res));
402 }
403 
404 } // namespace smtrat::subtropical
std::unordered_map< carl::Variable, Moment > m_moments
Maps a variable to the components of the moment function.
Definition: Subtropical.h:108
FormulaT encode_separator(const Separator &separator, const Direction direction, const SeparatorType separator_type)
Creates the formula for the hyperplane that linearly separates at least one variant positive frame ve...
Definition: Subtropical.h:120
Model construct_assignment(const Model &lra_model, const FormulaT &f) const
Definition: Subtropical.h:195
FormulaT encode_integer(carl::Variable var)
Definition: Subtropical.h:190
int var(Lit p)
Definition: SolverTypes.h:64
bool sign(Lit p)
Definition: SolverTypes.h:63
std::pair< CoveringResult< typename op::PropertiesSet >, std::vector< ParameterTree > > parameter(cadcells::datastructures::Projections &proj, FE &f, cadcells::Assignment ass, const VariableQuantification &quantification)
Definition: Algorithm.h:298
Numeric abs(const Numeric &_value)
Calculates the absolute value of the given Numeric.
Definition: Numeric.cpp:276
Numeric gcd(const Numeric &_valueA, const Numeric &_valueB)
Calculates the greatest common divisor of the two arguments.
Definition: Numeric.cpp:317
carl::Sign sgn(carl::Variable v, const Poly &p, const RAN &r)
Definition: utils.h:15
bool substitute(const FormulaT &constr, const carl::Variable var, const carl::vs::Term< Poly > &term, FormulaT &result)
Definition: VSHelper.h:138
Implements data structures and encodings for the subtropical method.
Definition: Subtropical.h:11
carl::BasicConstraint< Poly > normalize(const carl::BasicConstraint< Poly > &c)
Definition: Subtropical.h:57
std::optional< Direction > direction(carl::Relation r)
Definition: Subtropical.h:62
FormulaT encode_as_formula(const FormulaT &formula, Encoding &encoding, SeparatorType separator_type)
Requires a quantifier-free real arithmetic formula with no negations.
Definition: Subtropical.h:319
FormulaT transform_to_equation(const FormulaT &formula)
Requires a quantifier-free real arithmetic formula with no negations.
Definition: Subtropical.h:258
Direction
Subdivides the relations into classes with the same linearization result.
Definition: Subtropical.h:53
std::optional< Direction > direction_for_equality(const Separator &s)
Definition: Subtropical.h:96
FormulaT encode_as_formula_alt(const FormulaT &formula, Encoding &encoding, SeparatorType separator_type)
Requires a quantifier-free real arithmetic formula with no negations.
Definition: Subtropical.h:360
carl::Term< Rational > TermT
Definition: types.h:23
carl::Formula< Poly > FormulaT
Definition: types.h:37
carl::Model< Rational, Poly > Model
Definition: model.h:13
carl::MultivariatePolynomial< Rational > Poly
Definition: types.h:25
carl::Formulas< Poly > FormulasT
Definition: types.h:39
mpq_class Rational
Definition: types.h:19
Represents the normal vector component and the sign variable assigned to a variable in an original co...
Definition: Subtropical.h:21
const carl::Variable normal_vector
Normal vector component of the separating hyperplane.
Definition: Subtropical.h:23
const carl::Variable sign_variant
Boolean variable representing the sign variant.
Definition: Subtropical.h:25
Represents the class of all original constraints with the same left hand side after a normalization.
Definition: Subtropical.h:86
Separator(const Poly &normalization)
Definition: Subtropical.h:92
const carl::Variable bias
Bias variable of the separating hyperplane.
Definition: Subtropical.h:88
const std::vector< Vertex > vertices
Vertices for all terms of the normalized left hand side.
Definition: Subtropical.h:90
Represents a term of an original constraint and assigns him a rating variable if a weak separator is ...
Definition: Subtropical.h:35
carl::Variable rating() const
Definition: Subtropical.h:46
const Rational coefficient
Coefficient of the assigned term.
Definition: Subtropical.h:37
const carl::Monomial::Arg monomial
Monomial of the assigned term.
Definition: Subtropical.h:39
Vertex(const TermT &term)
Definition: Subtropical.h:43
carl::Variable m_rating
Rating variable of the term for a weak separator.
Definition: Subtropical.h:41