carl  24.04
Computer ARithmetic Library
Evaluation.h
Go to the documentation of this file.
1 #pragma once
2 
8 
9 #include "Ran.h"
11 
12 #include <boost/logic/tribool_io.hpp>
13 #include <optional>
14 
16 
17 namespace carl {
18 
19 /**
20  * Evaluate the given polynomial with the given values for the variables.
21  * Asserts that all variables of p have an assignment in m and that m has no additional assignments.
22  *
23  * Returns std::nullopt if some unassigned variables are still contained in p after plugging in m.
24  *
25  * @param p Polynomial to be evaluated
26  * @param m Variable assignment
27  * @return Evaluation result
28  */
29 template<typename Number>
30 std::optional<IntRepRealAlgebraicNumber<Number>> evaluate(MultivariatePolynomial<Number> p, const Assignment<IntRepRealAlgebraicNumber<Number>>& m, bool refine_model = true) {
31  CARL_LOG_DEBUG("carl.ran.interval", "Evaluating " << p << " on " << m);
32 
33  CARL_LOG_TRACE("carl.ran.interval", "Substitute rationals");
34  for (const auto& [var, ran] : m) {
35  if (!p.has(var)) continue;
36  if (refine_model) {
37  CARL_LOG_TRACE("carl.ran.interval", "Refine " << var << " = " << ran);
38  static Number min_width = Number(1) / (Number(1048576)); // 1/2^20, taken from libpoly
39  while (!ran.is_numeric() && ran.interval().diameter() > min_width) {
40  ran.refine();
41  }
42  }
43  if (ran.is_numeric()) {
44  CARL_LOG_TRACE("carl.ran.interval", "Substitute " << var << " = " << ran);
46  }
47  }
48  if (p.is_number()) {
49  CARL_LOG_DEBUG("carl.ran.interval", "Returning " << p.constant_part());
51  }
52 
53  CARL_LOG_TRACE("carl.ran.interval", "Remaing polynomial: " << p);
54 
55  CARL_LOG_TRACE("carl.ran.interval", "Create interval map");
56  std::map<Variable, Interval<Number>> var_to_interval;
57  for (const auto& [var, ran] : m) {
58  if (p.has(var)) {
59  assert(!ran.is_numeric());
60  var_to_interval.emplace(var, ran.interval());
61  }
62  }
63  CARL_LOG_TRACE("carl.ran.interval", "Interval map: " << var_to_interval);
64 
65  assert(!var_to_interval.empty());
66  if (var_to_interval.size() == 1) {
67  CARL_LOG_TRACE("carl.ran.interval", "Single interval");
68  auto poly = carl::to_univariate_polynomial(p);
69  assert(poly.main_var() == var_to_interval.begin()->first);
70  CARL_LOG_TRACE("carl.ran.interval", "Consider univariate poly " << poly);
71  if (sgn(m.at(var_to_interval.begin()->first), poly) == Sign::ZERO) {
72  CARL_LOG_DEBUG("carl.ran.interval", "Returning " << IntRepRealAlgebraicNumber<Number>());
74  }
75  }
76 
77  CARL_LOG_TRACE("carl.ran.interval", "Interval evaluation");
78  Interval<Number> interval = carl::evaluate(p, var_to_interval);
79 
80  if (interval.is_point_interval()) {
81  CARL_LOG_DEBUG("carl.ran.interval", "Interval is point interval " << interval);
82  return IntRepRealAlgebraicNumber<Number>(interval.lower());
83  }
84 
85  CARL_LOG_TRACE("carl.ran.interval", "Compute result polynomial");
86  static Variable v = fresh_real_variable();
87  std::vector<UnivariatePolynomial<MultivariatePolynomial<Number>>> algebraic_information;
88  for (const auto& [var, ran] : m) {
89  if (var_to_interval.find(var) == var_to_interval.end()) continue;
90  assert(!ran.is_numeric());
91  algebraic_information.emplace_back(replace_main_variable(ran.polynomial_int(), var).template convert<MultivariatePolynomial<Number>>());
92  }
93  // substitute RANs with low degrees first
94  std::sort(algebraic_information.begin(), algebraic_information.end(), [](const auto& a, const auto& b){
95  return a.degree() > b.degree();
96  });
98  if (!res) {
99  return std::nullopt;
100  }
101  res = carl::squareFreePart(*res);
102  // Note that res cannot be zero as v is a fresh variable in v-p.
103 
104  CARL_LOG_TRACE("carl.ran.interval", "res = " << *res);
105  CARL_LOG_TRACE("carl.ran.interval", "var_to_interval = " << var_to_interval);
106  CARL_LOG_TRACE("carl.ran.interval", "p = " << p);
107  CARL_LOG_TRACE("carl.ran.interval", "-> " << interval);
108 
109  CARL_LOG_TRACE("carl.ran.interval", "Compute sturm sequence");
110  auto sturm_seq = sturm_sequence(*res);
111  // the interval should include at least one root.
112  CARL_LOG_TRACE("carl.ran.interval", "Refine intervals");
113  assert(!carl::is_zero(*res));
114  assert(carl::is_root_of(*res, interval.lower()) || carl::is_root_of(*res, interval.upper()) || count_real_roots(sturm_seq, interval) >= 1);
115  while (!interval.is_point_interval() && (carl::is_root_of(*res, interval.lower()) || carl::is_root_of(*res, interval.upper()) || count_real_roots(sturm_seq, interval) != 1)) {
116  CARL_LOG_TRACE("carl.ran.interval", "Refinement step");
117  // refine the result interval until it isolates exactly one real root of the result polynomial
118  for (const auto& [var, ran] : m) {
119  if (var_to_interval.find(var) == var_to_interval.end()) continue;
120  ran.refine();
121  if (ran.is_numeric()) {
123  for (const auto& entry : m) {
124  if (!p.has(entry.first)) var_to_interval.erase(entry.first);
125  }
126  } else {
127  var_to_interval[var] = ran.interval();
128  }
129  }
130  CARL_LOG_TRACE("carl.ran.interval", "Interval evaluation");
131  interval = carl::evaluate(p, var_to_interval);
132  }
133  CARL_LOG_DEBUG("carl.ran.interval", "Result is " << *res << " " << interval);
134  if (interval.is_point_interval()) {
135  return IntRepRealAlgebraicNumber<Number>(interval.lower());
136  } else {
137  return IntRepRealAlgebraicNumber<Number>(*res, interval);
138  }
139 }
140 
141 template<typename Number>
142 boost::tribool evaluate(const BasicConstraint<MultivariatePolynomial<Number>>& c, const Assignment<IntRepRealAlgebraicNumber<Number>>& m, bool refine_model = true, bool use_root_bounds = true) {
143  CARL_LOG_DEBUG("carl.ran.interval", "Evaluating " << c << " on " << m);
144 
145  if (!use_root_bounds) {
146  CARL_LOG_DEBUG("carl.ran.interval", "Evaluate constraint by evaluating poly");
147  auto res = evaluate(c.lhs(), m);
148  if (!res) return boost::indeterminate;
149  else return evaluate(sgn(res), c.relation());
150  } else {
151  MultivariatePolynomial<Number> p = c.lhs();
152 
153  CARL_LOG_TRACE("carl.ran.interval", "p = " << p);
154 
155  for (const auto& [var, ran] : m) {
156  if (!p.has(var)) continue;
157  if (refine_model) {
158  static Number min_width = Number(1) / (Number(1048576)); // 1/2^20, taken from libpoly
159  while (!ran.is_numeric() && ran.interval().diameter() > min_width) {
160  ran.refine();
161  }
162  }
163  if (ran.is_numeric()) {
165  CARL_LOG_TRACE("carl.ran.interval", "Substituting numeric value p["<<ran.value()<<"/"<<var<<"] = " << p);
166  }
167  }
168 
169  if (p.is_number()) {
170  CARL_LOG_DEBUG("carl.ran.interval", "Left hand side is constant");
171  return carl::evaluate(p.constant_part(), c.relation());
172  }
174  if (constr.is_consistent() != 2) {
175  CARL_LOG_DEBUG("carl.ran.interval", "Constraint already evaluates to value");
176  return constr.is_consistent();
177  }
178  p = constr.lhs();
179  CARL_LOG_TRACE("carl.ran.interval", "p = " << p << " (after simplification)");
180 
181  std::map<Variable, Interval<Number>> var_to_interval;
182  for (const auto& [var, ran] : m) {
183  if (p.has(var)) {
184  assert(!ran.is_numeric());
185  var_to_interval.emplace(var, ran.interval());
186  }
187  }
188 
189  Interval<Number> interval = carl::evaluate(p, var_to_interval);
190  {
191  CARL_LOG_TRACE("carl.ran.interval", "Interval evaluation of " << p << " under " << var_to_interval << " results in " << interval);
192  auto int_res = carl::evaluate(interval, constr.relation());
193  CARL_LOG_TRACE("carl.ran.interval", "Obtained " << interval << " " << constr.relation() << " 0 -> " << (indeterminate(int_res) ? -1 : (bool)int_res));
194  if (!indeterminate(int_res)) {
195  CARL_LOG_DEBUG("carl.ran.interval", "Result obtained by interval evaluation");
196  return (bool)int_res;
197  }
198  }
199 
200  CARL_LOG_DEBUG("carl.ran.interval", "Evaluate constraint using resultants and root bounds");
201  assert(var_to_interval.size() > 0);
202  if (var_to_interval.size() == 1) {
203  CARL_LOG_TRACE("carl.ran.interval", "Single interval");
204  auto poly = carl::to_univariate_polynomial(p);
205  assert(poly.main_var() == var_to_interval.begin()->first);
206  CARL_LOG_TRACE("carl.ran.interval", "Consider univariate poly " << poly);
207  if (sgn(m.at(var_to_interval.begin()->first), poly) == Sign::ZERO) {
208  CARL_LOG_DEBUG("carl.ran.interval", "Got " << IntRepRealAlgebraicNumber<Number>());
209  return evaluate(Sign::ZERO, constr.relation());
210  }
211  }
212 
213  // compute the result polynomial
214  static Variable v = fresh_real_variable();
215  std::vector<UnivariatePolynomial<MultivariatePolynomial<Number>>> algebraic_information;
216  for (const auto& [var, ran] : m) {
217  if (var_to_interval.find(var) == var_to_interval.end()) continue;
218  assert(!ran.is_numeric());
219  algebraic_information.emplace_back(replace_main_variable(ran.polynomial_int(), var).template convert<MultivariatePolynomial<Number>>());
220  }
221  // substitute RANs with low degrees first
222  std::sort(algebraic_information.begin(), algebraic_information.end(), [](const auto& a, const auto& b){
223  return a.degree() > b.degree();
224  });
226  // Note that res cannot be zero as v is a fresh variable in v-p.
227  if (!res) {
228  return boost::indeterminate;
229  }
230 
231  CARL_LOG_DEBUG("carl.ran.interval", "res = " << *res);
232  CARL_LOG_DEBUG("carl.ran.interval", "var_to_interval = " << var_to_interval);
233  CARL_LOG_DEBUG("carl.ran.interval", "p = " << p);
234  CARL_LOG_DEBUG("carl.ran.interval", "-> " << interval);
235 
236  // Let pos_lb a lower bound on the positive real roots and neg_ub an upper bound on the negative real roots
237  // Then if the zero of res is in the interval (neg_ub,pos_lb), then it must be zero.
238 
239  // compute root bounds
240  auto pos_lb = lagrangePositiveLowerBound(*res);
241  CARL_LOG_TRACE("carl.ran.interval", "positive root lower bound: " << pos_lb);
242  if (pos_lb == 0) {
243  // no positive root exists
244  CARL_LOG_DEBUG("carl.ran.interval", "p <= 0");
245  if (constr.relation() == Relation::GREATER) {
246  return false;
247  } else if (constr.relation() == Relation::LEQ) {
248  return true;
249  }
250  }
251  auto neg_ub = lagrangeNegativeUpperBound(*res);
252  CARL_LOG_TRACE("carl.ran.interval", "negative root upper bound: " << neg_ub);
253  if (neg_ub == 0) {
254  // no negative root exists
255  CARL_LOG_DEBUG("carl.ran.interval", "p >= 0");
256  if (constr.relation() == Relation::LESS) {
257  return false;
258  } else if (constr.relation() == Relation::GEQ) {
259  return true;
260  }
261  }
262 
263  if (pos_lb == 0 && neg_ub == 0) {
264  // no positive or negative zero exists
265  CARL_LOG_DEBUG("carl.ran.interval", "p = 0");
266  return evaluate(Sign::ZERO, constr.relation());
267  }
268 
269  assert(!carl::is_zero(*res));
270 
271  // refine the interval until it is either positive or negative or is contained in (neg_ub,pos_lb)
272  CARL_LOG_DEBUG("carl.ran.interval", "Refine until interval is in (" << neg_ub << "," << pos_lb << ") or interval is positive or negative");
273  while (!((neg_ub < interval.lower() || neg_ub == 0) && (interval.upper() < pos_lb || pos_lb == 0))) {
274  for (const auto& [var, ran] : m) {
275  if (var_to_interval.find(var) == var_to_interval.end()) continue;
276  ran.refine();
277  if (ran.is_numeric()) {
279  for (const auto& entry : m) {
280  if (!p.has(entry.first)) var_to_interval.erase(entry.first);
281  }
282  } else {
283  var_to_interval[var] = ran.interval();
284  }
285  }
286  interval = carl::evaluate(p, var_to_interval);
287  auto int_res = carl::evaluate(interval, constr.relation());
288  if (!indeterminate(int_res)) {
289  CARL_LOG_DEBUG("carl.ran.interval", "Got result");
290  return (bool)int_res;
291  }
292  }
293 
294  CARL_LOG_DEBUG("carl.ran.interval", "p = 0");
295  return evaluate(Sign::ZERO, constr.relation());
296  }
297 }
298 
299 
300 template<typename Coeff, typename Ordering, typename Policies>
303 }
304 
305 template<typename Coeff, typename Ordering, typename Policies>
308 }
309 
310 }
This file contains carl::algebraic_substitution which performs what we call an algebraic substitution...
#define CARL_LOG_TRACE(channel, msg)
Definition: carl-logging.h:44
#define CARL_LOG_DEBUG(channel, msg)
Definition: carl-logging.h:43
carl is the main namespace for the library.
MultivariatePolynomial< C, O, P > squareFreePart(const MultivariatePolynomial< C, O, P > &polynomial)
UnivariatePolynomial< C > to_univariate_polynomial(const MultivariatePolynomial< C, O, P > &p)
Convert a univariate polynomial that is currently (mis)represented by a 'MultivariatePolynomial' into...
std::vector< UnivariatePolynomial< Coeff > > sturm_sequence(const UnivariatePolynomial< Coeff > &p, const UnivariatePolynomial< Coeff > &q)
Computes the sturm sequence of two polynomials.
Definition: SturmSequence.h:16
int count_real_roots(const std::vector< UnivariatePolynomial< Coefficient >> &seq, const Interval< Coefficient > &i)
Calculate the number of real roots of a polynomial within a given interval based on a sturm sequence ...
Definition: RootCounting.h:19
@ ZERO
Indicates that .
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
bool is_root_of(const UnivariatePolynomial< Coeff > &p, const Coeff &value)
Definition: Evaluation.h:62
UnivariatePolynomial< Coeff > replace_main_variable(const UnivariatePolynomial< Coeff > &p, Variable newVar)
Replaces the main variable in a polynomial.
Sign sgn(const Number &n)
Obtain the sign of the given number.
Definition: Sign.h:54
BasicConstraint< ToPoly > convert(const typename ToPoly::ContextType &context, const BasicConstraint< FromPoly > &c)
Definition: Conversion.h:9
Variable fresh_real_variable() noexcept
Definition: VariablePool.h:198
Coeff lagrangePositiveLowerBound(const UnivariatePolynomial< Coeff > &p)
Computes a lower bound on the value of the positive real roots of the given univariate polynomial.
Definition: RootBounds.h:114
@ GREATER
Definition: SignCondition.h:15
std::map< Variable, T > Assignment
Definition: Common.h:13
Coeff lagrangeNegativeUpperBound(const UnivariatePolynomial< Coeff > &p)
Computes an upper bound on the value of the negative real roots of the given univariate polynomial.
Definition: RootBounds.h:127
void substitute_inplace(MultivariateRoot< Poly > &mr, Variable var, const Poly &poly)
Create a copy of the underlying polynomial with the given variable replaced by the given polynomial.
BasicConstraint< Pol > create_normalized_constraint(const Pol &lhs, Relation rel)
std::optional< UnivariatePolynomial< Number > > algebraic_substitution(const UnivariatePolynomial< MultivariatePolynomial< Number >> &p, const std::vector< UnivariatePolynomial< MultivariatePolynomial< Number >>> &polynomials, AlgebraicSubstitutionStrategy strategy=AlgebraicSubstitutionStrategy::RESULTANT)
Computes the algebraic substitution of the given defining polynomials into a multivariate polynomial ...
Represent a polynomial (in)equality against zero.
const Pol & lhs() const
unsigned is_consistent() const
Relation relation() const
A Variable represents an algebraic variable that can be used throughout carl.
Definition: Variable.h:85
The class which contains the interval arithmetic including trigonometric functions.
Definition: Interval.h:134
const Number & upper() const
The getter for the upper boundary of the interval.
Definition: Interval.h:849
const Number & lower() const
The getter for the lower boundary of the interval.
Definition: Interval.h:840
bool is_point_interval() const
Function which determines, if the interval is a pointinterval.
Definition: Interval.h:1071
This class represents a univariate polynomial with coefficients of an arbitrary type.
const Coeff & constant_part() const
Retrieve the constant term of this polynomial or zero, if there is no constant term.
bool is_number() const
Check if the polynomial is a number, i.e., a constant.
const UnivariatePolynomial< MultivariatePolynomial< Coeff, Ordering, Policies > > & content() const
typename UnivariatePolynomial< NumberType >::RootType RootType