carl  24.04
Computer ARithmetic Library
Simplification.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "BasicConstraint.h"
4 #include "../poly/umvpoly/functions/Definiteness.h"
6 
7 namespace carl::constraint {
8 
9 static constexpr bool FULL_EFFORT_FOR_DEFINITENESS_CHECK = false;
10 
11 template<typename Pol>
12 BasicConstraint<Pol> init_bound(Variable var, Relation rel, const typename Pol::NumberType& bound) {
13  CARL_LOG_FUNC("carl.constraint", var << ", " << rel << ", " << bound);
14  auto lhs = Pol(var);
15  switch (rel) {
16  case Relation::GREATER:
17  lhs = -lhs;
18  if (var.type() == VariableType::VT_INT) {
19  if (is_integer(bound))
20  lhs += bound + typename Pol::NumberType(1);
21  else
22  lhs += carl::ceil(bound);
23  rel = Relation::LEQ;
24  } else {
25  lhs += bound;
26  rel = Relation::LESS;
27  }
28  break;
29  case Relation::GEQ:
30  lhs = -lhs;
31  if (var.type() == VariableType::VT_INT) {
32  if (is_integer(bound))
33  lhs += bound;
34  else
35  lhs += carl::ceil(bound);
36  rel = Relation::LEQ;
37  } else {
38  lhs += bound;
39  rel = Relation::LEQ;
40  }
41  break;
42  case Relation::LESS:
43  if (var.type() == VariableType::VT_INT) {
44  if (is_integer(bound))
45  lhs -= (bound - typename Pol::NumberType(1));
46  else
47  lhs -= carl::floor(bound);
48  rel = Relation::LEQ;
49  } else {
50  lhs -= bound;
51  }
52  break;
53  case Relation::LEQ:
54  if (var.type() == VariableType::VT_INT) {
55  if (is_integer(bound))
56  lhs -= bound;
57  else
58  lhs -= carl::floor(bound);
59  } else {
60  lhs -= bound;
61  }
62  break;
63  case Relation::EQ:
64  if (var.type() == VariableType::VT_INT) {
65  if (is_integer(bound)) {
66  lhs -= bound;
67  } else {
68  lhs = Pol(typename Pol::NumberType(0));
69  rel = Relation::LESS;
70  }
71  } else {
72  lhs -= bound;
73  }
74  break;
75  case Relation::NEQ:
76  if (var.type() == VariableType::VT_INT) {
77  if (is_integer(bound)) {
78  lhs -= bound;
79  } else {
80  lhs = Pol(typename Pol::NumberType(0));
81  rel = Relation::EQ;
82  }
83  } else {
84  lhs -= bound;
85  }
86  break;
87  }
88  return BasicConstraint<Pol>(std::move(lhs), rel);
89 }
90 
91 template<typename Pol>
93  CARL_LOG_FUNC("carl.core.constraint", lhs << ", " << rel);
94  if (lhs.is_constant()) {
95  CARL_LOG_TRACE("carl.core.constraint", lhs << " is constant, we simply evaluate.");
96  return evaluate(lhs.constant_part(), rel) ? BasicConstraint<Pol>(true) : BasicConstraint<Pol>(false);
97  } else if (lhs.total_degree() == 1 && (rel != Relation::EQ && rel != Relation::NEQ) && lhs.is_univariate()) {
98  if (carl::is_negative(lhs.lcoeff())) {
99  CARL_LOG_TRACE("carl.core.constraint", "Normalizing leading coefficient of linear poly.");
100  switch (rel) {
101  case Relation::LESS:
102  rel = Relation::GREATER;
103  break;
104  case Relation::GREATER:
105  rel = Relation::LESS;
106  break;
107  case Relation::LEQ:
108  rel = Relation::GEQ;
109  break;
110  case Relation::GEQ:
111  rel = Relation::LEQ;
112  break;
113  default:
114  assert(false);
115  break;
116  }
117  }
118  CARL_LOG_TRACE("carl.core.constraint", "Rewriting to bound");
119  return init_bound<Pol>(lhs.single_variable(), rel, (-lhs.constant_part()) / lhs.lcoeff());
120  } else {
121  CARL_LOG_TRACE("carl.core.constraint", "Normalizing " << lhs << " " << rel << " 0");
122  auto new_lhs = is_zero(lhs) ? Pol(typename Pol::NumberType(0)) : lhs.coprime_coefficients();
123  if (rel == Relation::EQ || rel == Relation::NEQ) {
124  if (!is_zero(new_lhs) && new_lhs.lterm().coeff() < typename Pol::NumberType(0)) new_lhs = -new_lhs;
125  } else if (rel == Relation::LEQ || rel == Relation::LESS) {
126  if (!is_zero(new_lhs) && (lhs.lterm().coeff() < 0) != (new_lhs.lterm().coeff() < 0)) new_lhs = -new_lhs;
127  } else if (rel == Relation::GREATER) {
128  if (!is_zero(new_lhs) && (lhs.lterm().coeff() < 0) == (new_lhs.lterm().coeff() < 0)) new_lhs = -new_lhs;
129  rel = Relation::LESS;
130  } else if (rel == Relation::GEQ) {
131  if (!is_zero(new_lhs) && (lhs.lterm().coeff() < 0) == (new_lhs.lterm().coeff() < 0)) new_lhs = -new_lhs;
132  rel = Relation::LEQ;
133  }
134 
135  return BasicConstraint<Pol>(std::move(new_lhs), rel);
136  }
137 }
138 
139 template<typename Pol>
141  auto vars = variables(constraint);
142  if (!vars.integer().empty() && vars.real().empty()) {
143  if (constraint.relation() == Relation::LESS) {
144  constraint.set_lhs(constraint.lhs() + carl::constant_one<typename Pol::CoeffType>::get());
145  constraint.set_relation(Relation::LEQ);
146  } else if (constraint.relation() == Relation::GREATER) {
147  constraint.set_lhs(constraint.lhs() - carl::constant_one<typename Pol::CoeffType>::get());
148  constraint.set_relation(Relation::GEQ);
149  }
150  }
151 }
152 
153 template<typename Pol>
154 inline unsigned is_consistent_definiteness(const BasicConstraint<Pol>& constraint, std::optional<Definiteness> lhs_definiteness = std::nullopt) {
155  if (constraint.lhs().is_constant()) {
156  CARL_LOG_TRACE("carl.formula.constraint", "Lhs " << constraint.lhs() << " is constant");
157  return carl::evaluate(constraint.lhs().constant_part(), constraint.relation()) ? 1 : 0;
158  } else {
159  if (!lhs_definiteness) {
160  lhs_definiteness = carl::definiteness(constraint.lhs(), FULL_EFFORT_FOR_DEFINITENESS_CHECK);
161  }
162  CARL_LOG_TRACE("carl.formula.constraint", "Checking " << constraint.relation() << " against " << *lhs_definiteness);
163  switch (constraint.relation()) {
164  case Relation::EQ: {
165  if (*lhs_definiteness == Definiteness::POSITIVE || *lhs_definiteness == Definiteness::NEGATIVE) return 0;
166  break;
167  }
168  case Relation::NEQ: {
169  if (*lhs_definiteness == Definiteness::POSITIVE || *lhs_definiteness == Definiteness::NEGATIVE) return 1;
170  break;
171  }
172  case Relation::LESS: {
173  if (*lhs_definiteness == Definiteness::NEGATIVE) return 1;
174  if (*lhs_definiteness >= Definiteness::POSITIVE_SEMI) return 0;
175  break;
176  }
177  case Relation::GREATER: {
178  if (*lhs_definiteness == Definiteness::POSITIVE) return 1;
179  if (*lhs_definiteness <= Definiteness::NEGATIVE_SEMI) return 0;
180  break;
181  }
182  case Relation::LEQ: {
183  if (*lhs_definiteness <= Definiteness::NEGATIVE_SEMI) return 1;
184  if (*lhs_definiteness == Definiteness::POSITIVE) return 0;
185  break;
186  }
187  case Relation::GEQ: {
188  if (*lhs_definiteness >= Definiteness::POSITIVE_SEMI) return 1;
189  if (*lhs_definiteness == Definiteness::NEGATIVE) return 0;
190  break;
191  }
192  default:
193  assert(false);
194  return false;
195  }
196  return 2;
197  }
198 }
199 
200 template<typename Pol>
201 inline void normalize_consistency_inplace(BasicConstraint<Pol>& constraint, std::optional<Definiteness> lhs_definiteness = std::nullopt) {
202  unsigned consistency = is_consistent_definiteness(constraint, lhs_definiteness);
203  if (consistency == 0) {
204  constraint = BasicConstraint<Pol>(false);
205  } else if (consistency == 1) {
206  constraint = BasicConstraint<Pol>(true);
207  }
208 }
209 
210 template<typename Pol>
211 inline bool simplify_nonlinear_univariate_monomial_inplace(BasicConstraint<Pol>& constraint, std::optional<Definiteness> lhs_definiteness = std::nullopt) {
212  using PolyT = typename Pol::PolyType;
213 
214  auto vars = variables(constraint);
215  if (!(vars.size() == 1 && !constraint.lhs().is_linear() && constraint.lhs().nr_terms() == 1)) return false;
216 
217  if (!lhs_definiteness) {
218  lhs_definiteness = carl::definiteness(constraint.lhs(), FULL_EFFORT_FOR_DEFINITENESS_CHECK);
219  }
220 
221  // Left-hand side is a non-linear univariate monomial
222  Relation rel = constraint.relation();
223  if ((*lhs_definiteness == Definiteness::POSITIVE_SEMI && rel == Relation::LEQ) || (*lhs_definiteness == Definiteness::NEGATIVE_SEMI && rel == Relation::GEQ))
224  rel = Relation::EQ;
225  Variable var = vars.as_vector()[0];
226 
227  switch (rel) {
228  case Relation::EQ:
229  constraint = BasicConstraint<Pol>(PolyT(var), rel);
230  break;
231  case Relation::NEQ:
232  constraint = BasicConstraint<Pol>(PolyT(var), rel);
233  break;
234  case Relation::LEQ:
235  if (*lhs_definiteness == Definiteness::NEGATIVE_SEMI) {
236  constraint = init_constraint(PolyT(typename Pol::NumberType(-1)) * PolyT(var) * PolyT(var), rel);
237  } else {
238  constraint = init_constraint((constraint.lhs().trailingTerm().coeff() > 0 ? PolyT(typename Pol::NumberType(1)) : PolyT(typename Pol::NumberType(-1))) * PolyT(var), rel);
239  }
240  break;
241  case Relation::GEQ:
242  if (*lhs_definiteness == Definiteness::POSITIVE_SEMI) {
243  constraint = init_constraint(PolyT(var) * PolyT(var), rel);
244  } else {
245  constraint = init_constraint((constraint.lhs().trailingTerm().coeff() > 0 ? PolyT(typename Pol::NumberType(1)) : PolyT(typename Pol::NumberType(-1))) * PolyT(var), rel);
246  }
247  break;
248  case Relation::LESS:
249  if (*lhs_definiteness == Definiteness::NEGATIVE_SEMI) {
250  constraint = init_constraint(PolyT(var), Relation::NEQ);
251  } else if (*lhs_definiteness == Definiteness::POSITIVE_SEMI) {
252  constraint = init_constraint(PolyT(var) * PolyT(var), rel);
253  } else {
254  constraint = init_constraint((constraint.lhs().trailingTerm().coeff() > 0 ? PolyT(typename Pol::NumberType(1)) : PolyT(typename Pol::NumberType(-1))) * PolyT(var), rel);
255  }
256  break;
257  case Relation::GREATER:
258  if (*lhs_definiteness == Definiteness::POSITIVE_SEMI) {
259  constraint = init_constraint(PolyT(var), Relation::NEQ);
260  } else if (*lhs_definiteness == Definiteness::NEGATIVE_SEMI) {
261  constraint = init_constraint(PolyT(typename Pol::NumberType(-1)) * PolyT(var) * PolyT(var), rel);
262  } else {
263  constraint = init_constraint((constraint.lhs().trailingTerm().coeff() > 0 ? PolyT(typename Pol::NumberType(1)) : PolyT(typename Pol::NumberType(-1))) * PolyT(var), rel);
264  }
265  break;
266  default:
267  assert(false);
268  }
269 
270  return true;
271 }
272 
273 template<typename Pol>
275  auto vars = variables(constraint);
276 
277  if (vars.integer().empty() || !vars.real().empty()) return false;
278 
279  typename Pol::NumberType constPart = constraint.lhs().constant_part();
280  if (constPart != typename Pol::NumberType(0)) {
281  // Find the gcd of the coefficients of the non-constant terms.
282  typename Pol::NumberType g = carl::abs(constraint.lhs().coprime_factor_without_constant());
283  assert(g != typename Pol::NumberType(0));
284  if (carl::mod(carl::get_num(constPart), carl::get_denom(g)) != 0) {
285  switch (constraint.relation()) {
286  case Relation::EQ: {
287  constraint = BasicConstraint<Pol>(Pol(typename Pol::NumberType(0)), Relation::LESS);
288  break;
289  }
290  case Relation::NEQ: {
291  constraint = BasicConstraint<Pol>(Pol(typename Pol::NumberType(0)), Relation::EQ);
292  break;
293  }
294  case Relation::LEQ: {
295  Pol newLhs = ((constraint.lhs() - constPart) * g);
296  newLhs += carl::floor((constPart * g)) + typename Pol::NumberType(1);
297  constraint = init_constraint(newLhs, Relation::LEQ);
298  break;
299  }
300  case Relation::GEQ: {
301  Pol newLhs = ((constraint.lhs() - constPart) * g);
302  newLhs += carl::floor((constPart * g));
303  constraint = init_constraint(newLhs, Relation::GEQ);
304  break;
305  }
306  case Relation::LESS: {
307  Pol newLhs = ((constraint.lhs() - constPart) * g);
308  newLhs += carl::floor((constPart * g)) + typename Pol::NumberType(1);
309  constraint = init_constraint(newLhs, Relation::LEQ);
310  break;
311  }
312  case Relation::GREATER: {
313  Pol newLhs = ((constraint.lhs() - constPart) * g);
314  newLhs += carl::floor((constPart * g));
315  constraint = init_constraint(newLhs, Relation::GEQ);
316  break;
317  }
318  default:
319  assert(false);
320  }
321  return true;
322  }
323  }
324  return false;
325 }
326 
327 template<typename Pol>
328 BasicConstraint<Pol> create_normalized_bound(Variable var, Relation rel, const typename Pol::NumberType& bound) {
329  auto constraint = init_bound<Pol>(var,rel,bound);
330  normalize_integer_inplace(constraint);
331  auto lhs_definiteness = carl::definiteness(constraint.lhs(), FULL_EFFORT_FOR_DEFINITENESS_CHECK);
332  simplify_nonlinear_univariate_monomial_inplace(constraint, lhs_definiteness);
333  simplify_integer_inplace(constraint);
334  return constraint;
335 }
336 
337 
338 template<typename Pol>
340  auto constraint = init_constraint(lhs,rel);
341  normalize_integer_inplace(constraint);
342  auto lhs_definiteness = carl::definiteness(constraint.lhs(), FULL_EFFORT_FOR_DEFINITENESS_CHECK);
343  normalize_consistency_inplace(constraint, lhs_definiteness);
344  if (!constraint.is_trivial_true() && !constraint.is_trivial_false()) {
345  simplify_nonlinear_univariate_monomial_inplace(constraint, lhs_definiteness);
346  simplify_integer_inplace(constraint);
347  }
348  return constraint;
349 }
350 
351 
352 }
#define CARL_LOG_FUNC(channel, args)
Definition: carl-logging.h:46
#define CARL_LOG_TRACE(channel, msg)
Definition: carl-logging.h:44
MultivariatePolynomial< Rational > Pol
Definition: HornerTest.cpp:17
Interval< Number > ceil(const Interval< Number > &_in)
Method which returns the next larger integer of the passed number or the number itself,...
Definition: Interval.h:1535
Interval< Number > abs(const Interval< Number > &_in)
Method which returns the absolute value of the passed number.
Definition: Interval.h:1511
Interval< Number > floor(const Interval< Number > &_in)
Method which returns the next smaller integer of this number or the number itself,...
Definition: Interval.h:1523
cln::cl_I mod(const cln::cl_I &a, const cln::cl_I &b)
Calculate the remainder of the integer division.
Definition: operations.h:445
Definiteness definiteness(const Term< Coeff > &t)
Definition: Definiteness.h:46
bool is_zero(const Interval< Number > &i)
Check if this interval is a point-interval containing 0.
Definition: Interval.h:1453
bool evaluate(const BasicConstraint< Poly > &c, const Assignment< Number > &m)
Definition: Evaluation.h:10
cln::cl_I get_num(const cln::cl_RA &n)
Extract the numerator from a fraction.
Definition: operations.h:60
bool is_integer(const Interval< Number > &n)
Definition: Interval.h:1445
bool is_negative(const cln::cl_I &n)
Definition: operations.h:47
cln::cl_I get_denom(const cln::cl_RA &n)
Extract the denominator from a fraction.
Definition: operations.h:69
@ GREATER
Definition: SignCondition.h:15
Relation
Definition: Relation.h:20
@ NEGATIVE_SEMI
Indicates that .
@ NEGATIVE
Indicates that .
@ POSITIVE
Indicates that .
@ POSITIVE_SEMI
Indicates that .
void variables(const BasicConstraint< Pol > &c, carlVariables &vars)
unsigned is_consistent_definiteness(const BasicConstraint< Pol > &constraint, std::optional< Definiteness > lhs_definiteness=std::nullopt)
BasicConstraint< Pol > create_normalized_constraint(const Pol &lhs, Relation rel)
BasicConstraint< Pol > create_normalized_bound(Variable var, Relation rel, const typename Pol::NumberType &bound)
BasicConstraint< Pol > init_bound(Variable var, Relation rel, const typename Pol::NumberType &bound)
static constexpr bool FULL_EFFORT_FOR_DEFINITENESS_CHECK
Definition: Simplification.h:9
bool simplify_nonlinear_univariate_monomial_inplace(BasicConstraint< Pol > &constraint, std::optional< Definiteness > lhs_definiteness=std::nullopt)
bool simplify_integer_inplace(BasicConstraint< Pol > &constraint)
BasicConstraint< Pol > init_constraint(const Pol &lhs, Relation rel)
void normalize_integer_inplace(BasicConstraint< Pol > &constraint)
void normalize_consistency_inplace(BasicConstraint< Pol > &constraint, std::optional< Definiteness > lhs_definiteness=std::nullopt)
Represent a polynomial (in)equality against zero.
void set_relation(Relation rel)
const Pol & lhs() const
Relation relation() const
void set_lhs(Pol &&lhs)
A Variable represents an algebraic variable that can be used throughout carl.
Definition: Variable.h:85
constexpr VariableType type() const noexcept
Retrieves the type of the variable.
Definition: Variable.h:131