carl  24.04
Computer ARithmetic Library
ThomRootFinder.h
Go to the documentation of this file.
1 /*
2  * File: ThomRootFinder.h
3  * Author: tobias
4  *
5  * Created on 19. August 2016, 10:18
6  */
7 
8 #pragma once
9 
11 #include "ThomEncoding.h"
12 #include "ThomUtil.h"
13 #include "ran_thom.h"
14 
15 namespace carl {
16 
17 // forward declarations
18 template<typename Number>
19 class ThomEncoding;
20 template<typename Number>
22 
23 
24 template<typename Number>
25 std::list<ThomEncoding<Number>> realRootsThom(
27  Variable::Arg mainVar,
28  std::shared_ptr<ThomEncoding<Number>> point_ptr,
30 );
31 
32 /*
33  * Analyzes p and m in order to find a (recursive) Thom encoding on which p can be lifted
34  */
35 template<typename Number>
36 std::list<ThomEncoding<Number>> realRootsThom(
38  Variable::Arg mainVar,
39  const std::map<Variable, ThomEncoding<Number>>& m = {},
40  const Interval<Number>& interval = Interval<Number>::unbounded_interval()
41 ) {
42  CARL_LOG_INFO("carl.thom.rootfinder",
43  "\n---------------------------------------------\n"
44  << "Thom Root Finder called:\n"
45  << "---------------------------------------------\n"
46  << "p = " << p << " in " << mainVar << "\n"
47  << "m = " << m << "\n"
48  << "interval = " << interval << "\n"
49  << "---------------------------------------------");
50  CARL_LOG_ASSERT("carl.thom.rootfinder", !p.is_constant(), "this should not be handled here but somewhere before");
51  //CARL_LOG_ASSERT("carl.thom.rootfinder", p.has(mainVar), "");
52  // attention ...
53  if(!p.has(mainVar)) {
54  return std::list<ThomEncoding<Number>>();
55  }
56 
57  for(const auto& entry : m) {
58  CARL_LOG_ASSERT("carl.thom.rootfinder", entry.first == entry.second.main_var(), "invalid map Variable -> Thom encoding");
59  }
60  for(const auto& v : carl::variables(p)) {
61  if(v != mainVar) {
62  CARL_LOG_ASSERT("carl.thom.rootfinder", m.find(v) != m.end(), "there is a variable which is in p but not in m");
63  }
64  }
65 
66  if(p.is_univariate()) {
67  return realRootsThom<Number>(p, mainVar, nullptr, interval);
68  }
69  CARL_LOG_ASSERT("carl.thom.rootfinder", m.size() > 0, "");
70  CARL_LOG_ASSERT("carl.thom.rootfinder", m.size() == carl::variables(p).size() - 1, "");
71 
72  ThomEncoding<Number> point = ThomEncoding<Number>::analyzeTEMap(m);
73  std::shared_ptr<ThomEncoding<Number>> point_ptr = std::make_shared<ThomEncoding<Number>>(point);
74  return realRootsThom(p, mainVar, point_ptr, interval);
75 
76  /* In m, there is either one descending chain and a dimension-1 encoding or just one descending chain (but not quite sure about this) */
77 
78 // ThomEncoding<Number> point = std::max_element(m.begin(), m.end(),
79 // [](const std::pair<Variable, ThomEncoding<Number>>& lhs, const std::pair<Variable, ThomEncoding<Number>>& rhs) {
80 // return lhs.second.dimension() < rhs.second.dimension();
81 // }
82 // )->second;
83 // CARL_LOG_TRACE("carl.thom.rootfinder", "maximal encoding in m w.r.t. to dimension: " << point);
84 //
85 // // TODO assert that the encodings in the chain are actually the encodings in the map
86 //
87 // if(point.dimension() >= m.size()) {
88 // // in this case there is just one descending chain
89 // CARL_LOG_TRACE("carl.thom.rootfinder", "found single descending chain in m: " << point);
90 // std::shared_ptr<ThomEncoding<Number>> point_ptr = std::make_shared<ThomEncoding<Number>>(point);
91 // return realRootsThom(p, mainVar, point_ptr, interval);
92 // }
93 //
94 // else{
95 // for(const auto& entry : m) {
96 // CARL_LOG_ASSERT("carl.thom.rootfinder", entry.second.dimension() == 1, "this is an assumption i have made");
97 // }
98 // auto m_it = m.begin();
99 // point = m_it->second;
100 // m_it++;
101 // std::shared_ptr<ThomEncoding<Number>> point_ptr = std::make_shared<ThomEncoding<Number>>(point);
102 // while(m_it != m.end()) {
103 // ThomEncoding<Number> newPoint(m_it->second, point_ptr);
104 // point = newPoint;
105 // point_ptr = std::make_shared<ThomEncoding<Number>>(point);
106 // m_it++;
107 // }
108 // CARL_LOG_TRACE("carl.thom.rootfinder", "point = " << point);
109 // return realRootsThom(p, mainVar, point_ptr, interval);
110 // }
111 }
112 
113 /*
114  * don't call this directly
115  */
116 template<typename Number>
117 std::list<ThomEncoding<Number>> realRootsThom(
119  Variable::Arg mainVar,
120  std::shared_ptr<ThomEncoding<Number>> point_ptr,
121  const Interval<Number>& interval
122 ) {
123  using Polynomial = MultivariatePolynomial<Number>;
124  std::list<ThomEncoding<Number>> result;
125 
126 
127 
128  if(point_ptr == nullptr) {
129  std::list<Polynomial> derivatives = der(p, mainVar, 1, p.degree(mainVar));
130 
131  std::vector<Polynomial> zeroSet = {p};
132  SignDetermination<Number> sd(zeroSet.begin(), zeroSet.end());
133 
134  uint numOfRoots = sd.sizeOfZeroSet();
135  if(numOfRoots == 0) return {};
136 
137  std::list<SignCondition> signs = {};
138  auto it = derivatives.rbegin();
139  while(signs.size() < numOfRoots) {
140  signs = sd.getSignsAndAdd(*it);
141  it++;
142  }
143  std::shared_ptr<SignDetermination<Number>> sd_ptr = std::make_shared<SignDetermination<Number>>(sd);
144  for(const auto& sigma : signs) {
145  ThomEncoding<Number> newEncoding(
146  sigma,
147  p,
148  mainVar,
149  nullptr,
150  sd_ptr,
151  sigma.size());
152  result.push_back(newEncoding);
153  }
154 
155  }
156  else {
157  // check if p vanishes on point
158  if(point_ptr->makesPolynomialZero(p, mainVar)) {
159  return {ThomEncoding<Number>(Number(0), mainVar)};
160  }
161 
162  std::list<Polynomial> zeroSet = point_ptr->accumulatePolynomials();
163  zeroSet.push_front(p);
164 
165  SignDetermination<Number> sd(zeroSet.begin(), zeroSet.end());
166  uint numOfRoots = sd.sizeOfZeroSet();
167  if(numOfRoots == 0) return {};
168 
169  std::list<Polynomial> pointDerivatives = point_ptr->sd().processedPolynomials();
170  sd.getSignsAndAddAll(pointDerivatives.rbegin(), pointDerivatives.rend());
171 
172  std::list<Polynomial> pDerivatives = der(p, mainVar, 1, p.degree(mainVar));
173 
174  std::list<SignCondition> signs = {};
175  uint relevant = 0;
176  auto it = pDerivatives.rbegin();
177  while(signs.size() < numOfRoots) {
178  signs = sd.getSignsAndAdd(*it);
179  it++;
180  relevant++;
181  }
182 
183  std::shared_ptr<SignDetermination<Number>> sd_ptr = std::make_shared<SignDetermination<Number>>(sd);
184  SignCondition pointSigns = point_ptr->accumulateRelevantSigns();
185  for(const auto& sigma : signs) {
186  CARL_LOG_ASSERT("carl.thom.rootfinder", sigma.size() == pointSigns.size() + relevant, "");
187  if(pointSigns.isSuffixOf(sigma)) {
188  SignCondition newSigma(sigma);
189  newSigma.resize(relevant);
190  ThomEncoding<Number> newEncoding(
191  newSigma,
192  p,
193  mainVar,
194  point_ptr,
195  sd_ptr,
196  relevant
197  );
198  result.push_back(newEncoding);
199  }
200  }
201  }
202 
203  // convert in a vector because std::sort needs random access iterator
204  std::vector<ThomEncoding<Number>> result_vec(result.begin(), result.end());
205  std::sort(result_vec.begin(), result_vec.end());
206  result = std::list<ThomEncoding<Number>>(result_vec.begin(), result_vec.end());
207 
208  // check bounds
209  // this could be optimized e.g. using binary search ...
210  if(interval.lower_bound_type() == BoundType::STRICT) {
211  auto it = result.begin();
212  while(it != result.end() && *it <= interval.lower()) {
213  it = result.erase(it);
214  }
215  }
216  else if(interval.lower_bound_type() == BoundType::WEAK) {
217  auto it = result.begin();
218  while(it != result.end() && *it < interval.lower()) {
219  it = result.erase(it);
220  }
221  }
222 
223  if(interval.upper_bound_type() == BoundType::STRICT) {
224  std::reverse(result.begin(), result.end());
225  auto it = result.begin();
226  while(it != result.end() && *it >= interval.upper()) {
227  it = result.erase(it);;
228  }
229  std::reverse(result.begin(), result.end());
230  }
231  else if(interval.upper_bound_type() == BoundType::WEAK) {
232  std::reverse(result.begin(), result.end());
233  auto it = result.begin();
234  while(it != result.end() && *it > interval.upper()) {
235  it = result.erase(it);
236  }
237  std::reverse(result.begin(), result.end());
238  }
239 
240  CARL_LOG_INFO("carl.thom.rootfinder", "found the following roots: " << result);
241  return result;
242 }
243 
244 /*
245  * Interface to the Thom mechanism used in the CAD.
246  *
247  * Input:
248  * A (possibly multivariate) polynomial p represented with respect
249  * to the variable of the current level.
250  * A mapping m from a set of variables to a set of RANs that are either
251  * represented explicitly or as Thom encodings. All variables of p that
252  * are not the main variable must appear in m.
253  * An interval where we are looking for roots.
254  *
255  * Output:
256  * The list of RANs representing the real roots of the univariate polynomial
257  * obtained by substituting the coeffcients of p according to m.
258  *
259  * Overview:
260  * 1. Plug in all RANs in m which are explicitly represented into p
261  * 2. If the resulting polynomial has at most degree 2, try to solve trivial
262  * 3. Use the remaining Thom encodings in m to construct a real alg. point
263  * represented as a Thom encoding
264  * 4. Determine roots according to this point
265  */
266 
267 
268 template<typename Coeff, typename Number>
269 std::list<RealAlgebraicNumber<Number>> realRootsThom(
271  const std::map<Variable, RealAlgebraicNumber<Number>>& m,
272  const Interval<Number>& interval
273 ) {
274  CARL_LOG_TRACE("carl.thom.rootfinder", p << " in " << p.main_var() << ", " << m << ", " << interval);
275  assert(m.count(p.main_var()) == 0);
276  assert(!carl::is_zero(p));
277 
278  std::list<ThomEncoding<Number>> roots;
279 
281  std::map<Variable, ThomEncoding<Number>> TEmap;
282 
283  for (Variable v: variables(tmp)) {
284  if (v == p.main_var()) continue;
285  assert(m.count(v) > 0);
286  if (m.at(v).is_numeric()) {
287  substitute_inplace(tmp, v, Coeff(m.at(v).value()));
288  } else {
289  TEmap.emplace(v, m.at(v).getThomEncoding());
290  }
291  }
292  CARL_LOG_TRACE("carl.thom.rootfinder", "TEmap = " << TEmap);
293  CARL_LOG_TRACE("carl.thom.rootfinder", "tmp = " << tmp);
294  std::list<RealAlgebraicNumber<Number>> roots_triv;
295  if(variables(tmp).size() <= 1) {
296  if(carl::is_zero(tmp)) return {RealAlgebraicNumber<Number>(0)};
297  assert(variables(tmp).size() == 1);
298  // Coeff = MultivariatePolynomial<Number>, but all coefficients of tmp are numerical
299  UnivariatePolynomial<Number> tmp_univ = tmp.convert(std::function<Number(const Coeff&)>([](const Coeff& c){ assert(c.is_univariate()); return c.constant_part(); }));
300  if(tmp_univ.zero_is_root()) {
301  roots_triv.push_back(RealAlgebraicNumber<Number>(0));
302  tmp_univ.eliminateZeroRoots();
303  }
304  CARL_LOG_TRACE("carl.thom.rootfinder", "tmp_univ = " << tmp_univ);
305  switch (tmp_univ.degree()) {
306  case 0: {
307  CARL_LOG_TRACE("carl.thom.rootfinder", "roots_triv = " << roots_triv);
308  return roots_triv;
309  }
310  case 1: {
311  Number a = tmp_univ.coefficients()[1], b = tmp_univ.coefficients()[0];
312  assert(a != Number(0));
313  roots_triv.push_back(RealAlgebraicNumber<Number>(-b / a));
314  CARL_LOG_TRACE("carl.thom.rootfinder", "roots_triv = " << roots_triv);
315  return roots_triv;
316  }
317  case 2: {
318  Number a = tmp_univ.coefficients()[2], b = tmp_univ.coefficients()[1], c = tmp_univ.coefficients()[0];
319  assert(a != Number(0));
320  CARL_LOG_TRACE("carl.thom.rootfinder", "a = " << a << ", b = " << b << ", c = " << c);
321  /* Use this formulation of p-q-formula:
322  * x = ( -b +- \sqrt{ b*b - 4*a*c } ) / (2*a)
323  */
324  Number rad = b*b - 4*a*c;
325  if (rad == 0) {
326  roots_triv.push_back(RealAlgebraicNumber<Number>(-b / (2*a)));
327  CARL_LOG_TRACE("carl.thom.rootfinder", "roots_triv = " << roots_triv);
328  return roots_triv;
329  }
330  /*
331  else if (rad > 0) {
332  std::pair<Number, Number> res = carl::sqrt_fast(rad);
333  CARL_LOG_TRACE("carl.thom.rootfinder", "carl::sqrt_fast(rad) returned " << res);
334  if (res.first == res.second) {
335  // Root could be calculated exactly
336  roots_triv.push_back(RealAlgebraicNumber<Number>((-b - res.first) / (2*a)));
337  roots_triv.push_back(RealAlgebraicNumber<Number>((-b + res.first) / (2*a)));
338  CARL_LOG_TRACE("carl.thom.rootfinder", "roots_triv = " << roots_triv);
339  return roots_triv;
340  }
341  }
342  */
343  break;
344  }
345  }
346 
347  roots = realRootsThom<Number>(MultivariatePolynomial<Number>(tmp_univ), tmp_univ.main_var(), nullptr, interval);
348 
349  }
350  else {
351  // need to perform 'real' lifting
352 
353  MultivariatePolynomial<Number> p_multiv(tmp);
354  //std::cout << "p_multiv = " << p_multiv << std::endl;
355 
356  roots = realRootsThom(p_multiv, p.main_var(), TEmap, interval);
357  }
358 
359 
360 
361  std::list<RealAlgebraicNumber<Number>> res;
362  for(const auto& te : roots) res.push_back(RealAlgebraicNumber<Number>(te));
363  for(const auto& number : roots_triv) res.push_back(number);
364 
365  CARL_LOG_TRACE("carl.thom.rootfinder", "found the following roots: " << res);
366 
367  return res;
368 }
369 
370 
371 
372 } // namespace carl
#define CARL_LOG_TRACE(channel, msg)
Definition: carl-logging.h:44
#define CARL_LOG_ASSERT(channel, condition, msg)
Definition: carl-logging.h:47
#define CARL_LOG_INFO(channel, msg)
Definition: carl-logging.h:42
carl is the main namespace for the library.
std::uint64_t uint
Definition: numbers.h:16
bool is_zero(const Interval< Number > &i)
Check if this interval is a point-interval containing 0.
Definition: Interval.h:1453
std::list< MultivariatePolynomial< Number > > der(const MultivariatePolynomial< Number > &p, Variable::Arg var, uint from, uint upto)
Definition: ThomUtil.h:17
std::list< ThomEncoding< Number > > realRootsThom(const MultivariatePolynomial< Number > &p, Variable::Arg mainVar, std::shared_ptr< ThomEncoding< Number >> point_ptr, const Interval< Number > &interval=Interval< Number >::unbounded_interval())
typename UnderlyingNumberType< P >::type Coeff
@ WEAK
the given bound is compared by a weak ordering relation
@ STRICT
the given bound is compared by a strict ordering relation
void variables(const BasicConstraint< Pol > &c, carlVariables &vars)
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.
UnivariatePolynomial< Coefficient > reverse(UnivariatePolynomial< Coefficient > &&p)
Reverses the order of the coefficients of this polynomial.
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
BoundType lower_bound_type() const
The getter for the lower bound type of the interval.
Definition: Interval.h:883
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
BoundType upper_bound_type() const
The getter for the upper bound type of the interval.
Definition: Interval.h:892
static Interval< Number > unbounded_interval()
Method which returns the unbounded interval rooted at 0.
Definition: Interval.h:804
This class represents a univariate polynomial with coefficients of an arbitrary type.
const std::vector< Coefficient > & coefficients() const &
Retrieves the coefficients defining this polynomial.
Variable main_var() const
Retrieves the main variable of this polynomial.
UnivariatePolynomial< NewCoeff > convert() const
uint degree() const
Get the maximal exponent of the main variable.
bool zero_is_root() const
Checks if zero is a real root of this polynomial.
bool is_constant() const
Check if the polynomial is constant.
std::size_t degree(Variable::Arg var) const
Calculates the degree of this polynomial with respect to the given variable.
bool is_univariate() const
Checks whether only one variable occurs.
std::list< SignCondition > getSignsAndAddAll(InputIt first, InputIt last)
std::list< SignCondition > getSignsAndAdd(const Polynomial &p)
std::list< Polynomial > accumulatePolynomials() const
Definition: ThomEncoding.h:137
static ThomEncoding< Number > analyzeTEMap(const std::map< Variable, ThomEncoding< Number >> &m)
Definition: ThomEncoding.h:295