G+Smo  24.08.0
Geometry + Simulation Modules
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gsSturmSolver.h
1 
2 
3 #include <gsPolynomial/gsMonomialPoly.h>
4 
5 
6 namespace gismo{
7 
8 template<class T>
9 class gsSturmSolver
10 {
11 
12 
13 };
14 
18  template<typename T>
19  gsMatrix<T> FindRootIntervals(gsMonomialPoly<T> & poly, T eps)
20  {
21  gsMatrix<T> root(1, 3);
22  root.setZero();
23 
24  root = CalcIntervals(poly, eps, root);
25  root.conservativeResize(root.rows() - 1, 3);
26  root = SortRoots(root);
27 
28  return root;
29  }
30 
31 
34  template<typename T>
35  gsMatrix<T> CalcIntervals(const gsMonomialPoly<T> & poly, T eps, gsMatrix<T> rootIntervals)
36  {
37  gsMonomialPoly<T> copyPoly = poly;
38  gsVector<T> coeff = copyPoly.coefs();
39  int degree = copyPoly.basis().size() - 1;
40 
41  // checks if the input polynomial has 0 as a root
42  int divByX = 0;
43 
44  while (!copyPoly.isNull() && copyPoly.coefs()(0) == 0)
45  {
46  rootIntervals(rootIntervals.rows() - 1, 0) = 0;
47  rootIntervals(rootIntervals.rows() - 1, 1) = 0;
48  rootIntervals(rootIntervals.rows() - 1, 2) += 1;
49 
50  gsMonomialBasis<T> monomialBasis(1);
51  gsMatrix<T> coefs(2, 1);
52  coefs << 0, 1;
53  gsMonomialPoly<T> x(monomialBasis, coefs);
54  copyPoly = PolyDivision(copyPoly, x, 1);
55  divByX = 1;
56  }
57 
58  if (divByX == 1)
59  {
60  rootIntervals.conservativeResize(rootIntervals.rows() + 1, 3);
61  }
62 
63  //if the polynomial is not constant, the sturm sequence is calculated
64  if (! copyPoly.isConstant() )
65  {
66  gsMatrix<T> sturmSeq(degree + 1, degree + 1);
67  sturmSeq = SturmSequence(copyPoly);
68 
69  //checks if the polynomial has multiple roots
70  if (sturmSeq.bottomRows(1).isZero())
71  {
72  gsVector< T >vec = sturmSeq.row(sturmSeq.rows() - 2);
73  gsMonomialPoly<T >poly(copyPoly.basis(), vec);
74  rootIntervals = CalcIntervals(poly, eps, rootIntervals);
75  rootIntervals = CalcIntervals(PolyDivision(copyPoly, poly, 1), eps, rootIntervals);
76  }
77  // the polynomial doesn't have multiple roots
78  else{
79  RootIsolation(T(-CauchyBound(copyPoly)), CauchyBound(copyPoly),
80  eps, sturmSeq, rootIntervals);
81 
82  }
83  }
84  return rootIntervals;
85  }
86 
87 
89  template <typename T>
90  void RootIsolation(T leftBound, T rightBound, T eps, gsMatrix<T> & sturmSeq, gsMatrix<T> & rootIntervals){
91 
92  int roots = SignChanges(HornerEval(sturmSeq, leftBound)) - SignChanges(HornerEval(sturmSeq, rightBound));
93 
94  //no roots in this interval
95  if (roots == 0) { return; }
96 
97  //one root and the length of the intervals is <= 0.01
98  else if (roots == 1 && rightBound - leftBound <= 0.01)
99  {
100  rootIntervals(rootIntervals.rows() - 1, 0) = leftBound;
101  rootIntervals(rootIntervals.rows() - 1, 1) = rightBound;
102  rootIntervals(rootIntervals.rows() - 1, 2) = 1;
103  rootIntervals.conservativeResize(rootIntervals.rows() + 1, 3);
104  }
105  //more roots and the length of the interval is smaller than the border eps
106  else if (roots > 1 && rightBound - leftBound < eps)
107  {
108  rootIntervals(rootIntervals.rows() - 1, 0) = leftBound;
109  rootIntervals(rootIntervals.rows() - 1, 1) = rightBound;
110  rootIntervals(rootIntervals.rows() - 1, 2) = roots;
111  rootIntervals.conservativeResize(rootIntervals.rows() + 1, 3);
112  }
113 
114  //more roots and the length of the interval is greater than eps
115  else
116  {
117  T bound = (rightBound + leftBound) / 2;
118  gsVector<T> f = sturmSeq.row(0);
119  T root_check = HornerEvalPoly(f, bound);
120 
121  // the middle of the interval is a root
122  if (root_check == 0)
123  {
124  roots = SignChanges(HornerEval(sturmSeq, T(bound - eps/2)))
125  - SignChanges(HornerEval(sturmSeq, T(bound + eps/2)));
126  if (roots > 1)
127  {
128  rootIntervals(rootIntervals.rows() - 1, 0) = bound - eps / 2;
129  rootIntervals(rootIntervals.rows() - 1, 1) = bound + eps / 2;
130  rootIntervals(rootIntervals.rows() - 1, 2) = roots;
131  rootIntervals.conservativeResize(rootIntervals.rows() + 1, 3);
132  }
133  else
134  {
135  rootIntervals(rootIntervals.rows() - 1, 0) = bound;
136  rootIntervals(rootIntervals.rows() - 1, 1) = bound;
137  rootIntervals(rootIntervals.rows() - 1, 2) = 1;
138  rootIntervals.conservativeResize(rootIntervals.rows() + 1, 3);
139  }
140  RootIsolation(leftBound, T(bound - eps/2), eps, sturmSeq, rootIntervals);
141  RootIsolation(T(bound + eps/2), rightBound, eps, sturmSeq, rootIntervals);
142  }
143  // the middle of the interval is not a root, so a bisection is done
144  else
145  {
146  RootIsolation(leftBound, bound, eps, sturmSeq, rootIntervals);
147  RootIsolation(bound, rightBound, eps, sturmSeq, rootIntervals);
148 
149  }
150  }
151  }
152 
153 
155  template<typename T>
156  T CauchyBound(gsMonomialPoly<T> &poly)
157  {
158  gsVector<T> vec = poly.coefs();
159 
160  int lead = vec.size() - 1; // right-most non-zero
161  for (int i = vec.size() - 1; i >= 0; i--)
162  {
163  if (vec(i) != 0)
164  {
165  lead = i;
166  break;
167  }
168  }
169  return 1 + (vec/vec[lead]).array().abs().maxCoeff();
170  }
171 
173  template<typename T>
174  gsMatrix<T> SturmSequence(gsMonomialPoly<T> & poly)
175  {
176  int deg = poly.basis().size() - 1;
177  gsMatrix<T> sturm(deg + 1, deg + 1);
178  sturm.setZero();
179 
180  //first row = f
181  sturm.row(0) = poly.coefs().transpose();
182 
183  //second row = f'
184  sturm.row(1) = PolyDerivWithSameDegree(poly).coefs().transpose();
185 
186  if ( PolyDerivWithSameDegree(poly).isConstant() )
187  {
188  sturm.conservativeResize(2, sturm.cols());
189  return sturm;
190  }
191 
192  // i-th row = -rem(f_(i-2), f_(i-1))
193  // until it is constant
194  gsVector<T> dividend;
195  gsVector<T> divisor;
196 
197  for (int i = 2; i <= deg; i++)
198  {
199  dividend = sturm.row(i - 2);
200  divisor = sturm.row(i - 1);
201  gsMonomialPoly<T> poly1(poly.basis(), dividend);
202  gsMonomialPoly<T> poly2(poly.basis(), divisor);
203  gsMonomialPoly<T> poly3 = PolyDivision(poly1, poly2, 2);
204 
205  if ( poly3.isNull() )
206  {
207  sturm.conservativeResize(i + 1, sturm.cols());
208  return sturm;
209  }
210 
211 
212  for (int j = 0; j < poly3.coefs().size(); j++)
213  {
214  sturm(i, j) -= poly3.coefs()(j);
215  }
216 
217  if ( poly3.isConstant() )
218  {
219  sturm.conservativeResize(i + 1, sturm.cols());
220  return sturm;
221  }
222 
223  }
224 
225  return sturm;
226  }
227 
230  template<typename T>
231  gsMonomialPoly<T> PolyDerivWithSameDegree(const gsMonomialPoly<T> & poly)
232  {
233  gsVector<T> deriv(poly.deg()+1);
234  //AM:
235  //deriv.topRows(poly.deg()) = gsVector<int>::LinSpaced(poly.deg(),1,poly.deg()).array()
236  // * poly.coefs().bottomRows(poly.deg()).array();
237 
238  for (int i = 0; i < deriv.size() - 1; i++)
239  {
240  deriv(i) = poly.coefs()(i + 1)*(i + 1);
241  }
242  deriv(deriv.size() - 1) = 0;
243  return gsMonomialPoly<T>(poly.basis(), deriv);
244  }
245 
246 
251  template<typename T>
252  gsMonomialPoly<T> PolyDivision(const gsMonomialPoly<T> & dividendPoly,
253  const gsMonomialPoly<T> & divisorPoly, int num)
254  {
255  if (num != 1 && num != 2)
256  {
257  std::cout << "ERROR: The number is not in the corresponding range. The dividend will be returned" << std::endl;
258  return dividendPoly;
259  }
260  gsMonomialPoly<T> dividendCopy = dividendPoly;
261  gsMonomialPoly<T> divisorCopy = divisorPoly;
262 
263  // storing the coefs of dividend and divisor seperately in a gsVector/gsMatrix
264  gsMatrix<T> dividend = dividendCopy.coefs().transpose();
265 
266  gsVector<T> divisor(dividend.size());
267  divisor.setZero();
268  for (int i = 0; i < divisorCopy.coefs().size(); i++)
269  {
270  divisor(i) = divisorCopy.coefs()(i);
271  }
272 
273 
274  // getting the real degrees of dividend and divisor
275  int deg_dividend = dividend.size() - 1;
276 
277  while (dividend(deg_dividend) == 0)
278  {
279  deg_dividend--;
280  }
281 
282 
283  int deg_divisor = divisor.size() - 1;
284 
285  while (divisor(deg_divisor) == 0)
286  {
287  deg_divisor--;
288  }
289 
290  if (deg_divisor > deg_dividend)
291  {
292  std::cout << "Error, the division is not possible. The degree of the divisor is greater than the degree of the dividend. The dividend will be returned";
293  return dividendPoly;
294  }
295 
296 
297  // Algorithm for polynomial long division
298  gsVector<T> quot(dividend.size());
299  gsVector<T> rem(dividend.size());
300 
301  quot.setZero();
302  rem = dividend.transpose();
303  int deg_rem = deg_dividend;
304 
305  int lead = 0;
306  T lead_coeff = 0;
307 
308  gsVector<T> dummy;
309  while (!(rem.maxCoeff() == 0 && rem.minCoeff() == 0) && deg_rem >= deg_divisor)
310  {
311  lead = deg_rem - deg_divisor;
312  lead_coeff = rem(deg_rem) / divisor(deg_divisor);
313  quot(lead) = quot(lead) + lead_coeff;
314  dummy = ShiftRight(divisor, lead);
315  rem = rem - dummy * lead_coeff;
316  deg_rem = deg_dividend;
317 
318  while (rem(deg_rem) == 0 && deg_rem > 0)
319  {
320  deg_rem--;
321  }
322  }
323 
324  // returning the right output
325  if (num == 1){
326 
327  gsMonomialBasis<T> monomial_basis(deg_dividend - deg_divisor);
328  gsMonomialPoly<T> polyQuot(monomial_basis, quot.head(deg_dividend - deg_divisor + 1));
329 
330  return polyQuot;
331  }
332  else{
333 
334  gsMonomialBasis<T> monomial_basis(deg_rem);
335  gsMonomialPoly<T> polyRem(monomial_basis, rem.head(deg_rem + 1));
336 
337  return polyRem;
338  }
339 
340 
341  }
342 
345  template <typename T>
346  gsVector<T> ShiftRight(const gsVector<T> & vec, int num)
347  {
348  gsVector<T> shift_vec(vec.size());
349  shift_vec.topRows(num).setZero();
350  const index_t b = vec.size()-num;
351  shift_vec.bottomRows(b) = vec.topRows(b);
352  return shift_vec;
353  }
354 
355 
356  //returns the number of sign changes of a vector
357  template <typename T>
358  int SignChanges(const gsVector<T> & vec)
359  {
360  // AM:
361  //int result = 0;
362  //for (int i = 1; i < vec.size(); ++i)
363  // if (vec[i-1]*vec[i] .. 0) ++result;
364 
365  gsVector<T> vec_copy = vec;
366  for (int i = 0; i < vec.size(); i++)
367  {
368  vec_copy(i) = vec(i) / math::abs(vec(i));// 0, +1 or -1
369  }
370 
371  int result = 0;
372  for (int i = 0; i < vec_copy.size() - 1; i++)
373  {
374  if (math::abs(vec_copy(i) + vec_copy(i + 1)) < 2) { result++; }
375 
376  }
377  return result;
378  }
379 
380 
383  template <typename T>
385  {
386  int col = mat.cols();
387  gsVector<T> eval(mat.rows());
388  eval = mat.col(col - 1);
389 
390  for (int i = 2; i <= mat.cols(); i++)
391  {
392  eval = eval*num + mat.col(col - i);
393 
394  }
395  return eval;
396  }
397 
398 
400  template <typename T>
401  T HornerEvalPoly(const gsVector<T> & vec, T num)
402  {
403  T eval = vec(vec.size() - 1);
404 
405  for (int i = 2; i <= vec.size(); i++)
406  {
407  eval = eval*num + vec(vec.size() - i);
408  }
409  return eval;
410  }
411 
412 
415  template<typename T>
417  {
418  gsMatrix<T> sort(unsort.rows(), 3);
419  sort.setZero();
420  T min = -1;
421  int position = -1;
422 
423  for (int i = 0; i < unsort.rows(); i++)
424  {
425 
426  min = unsort.col(0).minCoeff();
427  for (int j = 0; j < unsort.rows(); j++)
428  {
429  if (min == unsort(j, 0)){
430  position = j;
431  break;
432  }
433  }
434 
435  sort.row(i) = unsort.row(position);
436  unsort(position, 0) = INT_MAX;
437  }
438 
439  for (int i = 0; i < sort.rows()-1; i++)
440  {
441 
442  if ((sort(i, 0) == sort(i + 1, 0) && sort(i, 1) <= sort(i + 1, 1)) ||
443  (sort(i, 0) <= sort(i + 1, 0) && sort(i, 1) == sort(i + 1, 1)))
444  {
445  sort(i, 2) += 1;
446  for (int j = i+1; j < sort.rows()-1; j++){
447  sort.row(j) = sort.row(j + 1);
448  }
449 
450  sort.conservativeResize(sort.rows()- 1, 3);
451  --i;
452  }
453  }
454  return sort;
455  }
456 
457 }
458 
459 
460 
461 
void RootIsolation(T leftBound, T rightBound, T eps, gsMatrix< T > &sturmSeq, gsMatrix< T > &rootIntervals)
Algorithm for the isolation of the roots.
Definition: gsSturmSolver.h:90
T CauchyBound(gsMonomialPoly< T > &poly)
returns the Cauchy Bound of a Polynomial, which is 1+max(abs(a_i/a_n))
Definition: gsSturmSolver.h:156
gsMonomialPoly< T > PolyDivision(const gsMonomialPoly< T > &dividendPoly, const gsMonomialPoly< T > &divisorPoly, int num)
Definition: gsSturmSolver.h:252
gsMatrix< T > CalcIntervals(const gsMonomialPoly< T > &poly, T eps, gsMatrix< T > rootIntervals)
Definition: gsSturmSolver.h:35
#define index_t
Definition: gsConfig.h:32
gsVector< T > ShiftRight(const gsVector< T > &vec, int num)
Definition: gsSturmSolver.h:346
An univariate monomial polynomial basis. If the degree is p the basis is given by: [ 1...
Definition: gsMonomialBasis.h:23
gsMonomialPoly< T > PolyDerivWithSameDegree(const gsMonomialPoly< T > &poly)
Definition: gsSturmSolver.h:231
gsMatrix< T > SortRoots(gsMatrix< T > &unsort)
Definition: gsSturmSolver.h:416
T HornerEvalPoly(const gsVector< T > &vec, T num)
evaluates a gsVector (which represents the coefficients of a polynomial) at value num ...
Definition: gsSturmSolver.h:401
gsMatrix< T > SturmSequence(gsMonomialPoly< T > &poly)
returns the sturm sequence in a gsMatrix
Definition: gsSturmSolver.h:174
EIGEN_STRONG_INLINE abs_expr< E > abs(const E &u)
Absolute value.
Definition: gsExpressions.h:4486
gsMatrix< T > FindRootIntervals(gsMonomialPoly< T > &poly, T eps)
Definition: gsSturmSolver.h:19
gsVector< T > HornerEval(gsMatrix< T > &mat, T num)
Definition: gsSturmSolver.h:384