stator
A math, geometry, and utility library
polynomial.hpp
Go to the documentation of this file.
1 
2 /*
3  Copyright (C) 2017 Marcus N Campbell Bannerman <[email protected]>
4 
5  This file is part of stator.
6 
7  stator is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  stator is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with stator. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #pragma once
22 
23 //stator
25 #include <stator/exception.hpp>
26 #include <stator/config.hpp>
27 
28 //C++
29 #include <stdexcept>
30 #include <ostream>
31 #include <array>
32 #include <tuple>
33 
34 namespace sym {
35  template<size_t Order, std::intmax_t num, std::intmax_t denom, class PolyVar>
36  class Polynomial<Order, C<num, denom>, PolyVar> {
37  static_assert(stator::detail::dependent_false<C<num, denom> >::value, "Cannot use C types as the coefficients of a polynomial");
38  };
39 
62  template<size_t Order, class Coeff_t, class PolyVar>
63  class Polynomial : public std::array<Coeff_t, Order+1>, SymbolicOperator
64  {
65  typedef std::array<Coeff_t, Order+1> Base;
66 
67  public:
68  using Base::operator[];
74  Polynomial() { Base::fill(empty_sum(Coeff_t())); }
75 
86  Polynomial(std::initializer_list<Coeff_t> _list) {
87  if (_list.size() > Order+1)
88  throw std::length_error("initializer list too long");
89 
90  size_t i = 0;
91  for (auto it = _list.begin(); it != _list.end(); ++i, ++it)
92  Base::operator[](i) = *it;
93 
94  for (; i <= Order; ++i)
95  Base::operator[](i) = empty_sum(Coeff_t());
96  }
97 
98  template<class InputIt>
99  Polynomial(InputIt first, InputIt last) {
100  size_t i = 0;
101  auto it = first;
102  for (; (it != last) && (i <= Order); ++i, ++it)
103  Base::operator[](i) = *it;
104 
105  if (it != last)
106  stator_throw() << "Polynomial type too short to hold this range of coefficients";
107 
108  for (; i <= Order; ++i)
109  Base::operator[](i) = empty_sum(Coeff_t());
110  }
111 
115  template<size_t N, class Coeff_t2, class PolyVar2,
116  typename = typename std::enable_if<(N <= Order) && variable_in<PolyVar, PolyVar2>::value >::type>
118  size_t i(0);
119  for (; i <= N; ++i)
120  Base::operator[](i) = poly[i];
121  for (; i <= Order; ++i)
122  Base::operator[](i) = empty_sum(Coeff_t());
123  }
124 
127  Polynomial retval;
128  for (size_t i(0); i <= Order; ++i)
129  retval[i] = -Base::operator[](i);
130  return retval;
131  }
132  };
133 
139  template<size_t NewOrder, size_t Order, class Coeff_t, class PolyVar>
141  //The default constructor blanks Polynomial coefficients to zero
143  //Just copy the coefficients which overlap between the new and old polynomial orders.
144  std::copy(f.begin(), f.begin() + std::min(Order, NewOrder) + 1, retval.begin());
145  return retval;
146  }
147 
158  template<size_t Order, class Coeff_t, class PolyVar>
161 
178  template<class Coeff_t, size_t Order, class Var1, class Var2, class ...VarArgs,
179  typename = typename enable_if_var_in<Var2, Var1>::type>
180  Polynomial<Order, Coeff_t, Var<VarArgs...> >
181  sub(const Polynomial<Order, Coeff_t, Var1>& f, const Relation<Var2, Var<VarArgs...> >& x_container)
182  { return Polynomial<Order, Coeff_t, Var<VarArgs...> >(f.begin(), f.end()); }
183 
186  template<size_t Order, class Coeff_t, class PolyVar, class SubVar,
187  typename = typename enable_if_var_in<PolyVar, SubVar>::type>
189  { return f[0]; }
190 
200  template<class Coeff_t, size_t Order, class PolyVar, class SubVar, class Coeff_t2,
201  typename = typename std::enable_if<(std::is_arithmetic<Coeff_t2>::value
202  && !std::is_base_of<Eigen::EigenBase<Coeff_t>, Coeff_t>::value
203  && !std::is_base_of<Eigen::EigenBase<Coeff_t2>, Coeff_t2>::value)>::type,
204  typename = typename enable_if_var_in<PolyVar, SubVar>::type>
205  decltype(store(Coeff_t() * Coeff_t2()))
207  {
208  //Handle the case where this is actually a constant and not a
209  //Polynomial. This is free to evaluate now as Order is a
210  //template parameter.
211  if (Order == 0)
212  return f[0];
213 
214  const auto & x = x_container._val;
215  //Special cases for infinite values of x
216  if (std::numeric_limits<Coeff_t2>::has_infinity
217  && ((std::numeric_limits<Coeff_t2>::infinity() == x)
218  || (std::numeric_limits<Coeff_t2>::infinity() == -x))) {
219  //Look through the Polynomial to find the highest order term
220  //with a non-zero coefficient.
221  for(size_t i = Order; i > 0; --i)
222  if (f[i] != empty_sum(f[0])) {
223  //Determine if this is an odd or even function of x
224  if (Order % 2)
225  //This is an odd function of x, the sign of x matters
226  return f[i] * x;
227  else
228  //This is an even function of x, its sign does not matter!
229  return f[i] * std::numeric_limits<Coeff_t2>::infinity();
230  };
231  //All terms in x have zero as their coefficient
232  return f[0];
233  }
234 
235 
236 
237  typedef decltype(store(Coeff_t() * Coeff_t2())) RetType;
238  RetType sum = RetType();
239  for(size_t i = Order; i > 0; --i)
240  sum = sum * x + f[i];
241  sum = sum * x + f[0];
242  return sum;
243  }
244 
245  namespace detail {
249  template<size_t Stage>
250  struct PolySubWorker {
251  template<size_t Order, class PolyVar, class Coeff_t, class X>
252  static auto eval(const Polynomial<Order, Coeff_t, PolyVar>& f, const X& x)
253  -> STATOR_AUTORETURN(f[Order - Stage] + x * PolySubWorker<Stage - 1>::eval(f, x));
254  };
255 
261  template<>
262  struct PolySubWorker<0> {
263  template<size_t Order, class PolyVar, class Coeff_t, class X>
264  static auto eval(const Polynomial<Order, Coeff_t, PolyVar>& f, const X& x)
265  -> STATOR_AUTORETURN(f[Order]);
266  };
267  }
268 
279  template<class Coeff_t, size_t Order, class PolyVar, class SubVar, class Coeff_t2,
280  typename = typename std::enable_if<!std::is_arithmetic<Coeff_t2>::value
281  || (std::is_base_of<Eigen::EigenBase<Coeff_t2>, Coeff_t2>::value && std::is_arithmetic<Coeff_t2>::value)
282  || (std::is_base_of<Eigen::EigenBase<Coeff_t>, Coeff_t>::value && std::is_arithmetic<Coeff_t2>::value)
283  >::type,
284  typename = typename enable_if_var_in<PolyVar, SubVar>::type>
287 
288 
295  template<size_t D, size_t Order, class Coeff_t, class PolyVar, class Coeff_t2>
296  std::array<Coeff_t, D+1> eval_derivatives(const Polynomial<Order, Coeff_t, PolyVar>& f, const Coeff_t2& x)
297  {
298  std::array<Coeff_t, D+1> retval;
299  retval.fill(empty_sum(Coeff_t()));
300  retval[0] = f[Order];
301  for (size_t i(Order); i>0; --i) {
302  for (size_t j = std::min(D, Order-(i-1)); j>0; --j)
303  retval[j] = retval[j] * x + retval[j-1];
304  retval[0] = retval[0] * x + f[i-1];
305  }
306 
307  Coeff_t cnst(1.0);
308  for (size_t i(2); i <= D; ++i) {
309  cnst *= i;
310  retval[i] *= cnst;
311  }
312 
313  return retval;
314  }
315 
337  template<size_t Order1, class Coeff_t, class PolyVar1, class PolyVar2, size_t Order2,
338  typename = typename enable_if_var_in<PolyVar1, PolyVar2>::type>
339  std::tuple<Polynomial<Order1, Coeff_t, typename variable_combine<PolyVar1, PolyVar2>::type>, Polynomial<Order2 - 1, Coeff_t, typename variable_combine<PolyVar1, PolyVar2>::type> >
341  {
342  static_assert(Order2 < Order1, "Cannot perform division when the order of the denominator is larger than the numerator using this routine");
343  static_assert(Order2 > 0, "Constant division fails with these loops");
344  typedef std::tuple<Polynomial<Order1, Coeff_t, typename variable_combine<PolyVar1, PolyVar2>::type>, Polynomial<Order2 - 1, Coeff_t, typename variable_combine<PolyVar1, PolyVar2>::type> > RetType;
345  //If the leading term of g is zero, drop to a lower order
346  //euclidean division.
347  if (g[Order2] == 0) {
348  auto lower_order_op = gcd(f, change_order<Order2 - 1>(g));
349  return RetType(std::get<0>(lower_order_op), std::get<1>(lower_order_op));
350  }
351 
352  //The quotient and remainder.
355 
356  //Loop from the highest order coefficient of f, down to where we
357  //have a polynomial one order lower than g.
358  for (size_t k(Order1); k>=Order2; --k) {
359  //Calculate the term on the quotient
360  q[k-Order2] = r[k] / g[Order2];
361  //Subtract this factor of other terms from the remainder
362  for (size_t j(0); j <= Order2; j++)
363  r[k+j-Order2] -= q[k-Order2] * g[j];
364  }
365 
366  return RetType(q, change_order<Order2 - 1>(r));
367  }
368 
372  template<size_t Order1, class Coeff_t, class PolyVar1, class PolyVar2,
373  typename = typename enable_if_var_in<PolyVar1, PolyVar2>::type>
374  std::tuple<Polynomial<Order1, Coeff_t, typename variable_combine<PolyVar1, PolyVar2>::type>, Polynomial<0, Coeff_t, typename variable_combine<PolyVar1, PolyVar2>::type> >
376  {
379  typedef std::tuple<FType, RType> RetType;
380  if (g[0] == 0)
381  return RetType(FType{std::numeric_limits<Coeff_t>::infinity()}, RType{empty_sum(Coeff_t())});
382 
383  return RetType(expand(f * (1.0 / g[0])), RType{empty_sum(Coeff_t())});
384  }
385 
399  template<class PolyVar, class ...VarArgs, class Coeff_t, size_t N,
400  typename = typename std::enable_if<(N > 0) && (variable_in<PolyVar, Var<VarArgs...> >::value)>::type,
401  typename = typename enable_if_var_in<PolyVar, Var<VarArgs...> >::type>
402  inline Polynomial<N-1, Coeff_t, typename variable_combine<PolyVar, Var<VarArgs...>>::type> derivative(const Polynomial<N, Coeff_t, PolyVar>& f, Var<VarArgs...>) {
403  Polynomial<N-1, Coeff_t, typename variable_combine<PolyVar, Var<VarArgs...> >::type> retval;
404  for (size_t i(0); i < N; ++i) {
405  retval[i] = f[i+1] * (i+1);
406  }
407  return retval;
408  }
409 
415  template<class PolyVar, class ...VarArgs, class Coeff_t, size_t N,
416  typename = typename std::enable_if<(N==0) || (!variable_in<PolyVar, Var<VarArgs...> >::value)>::type>
418  { return Null(); }
419 
457  template<size_t Order, class Coeff_t, class PolyVar>
459  {
460  //Check for the simple case where t == 0, nothing to be done
461  if (t == 0) return f;
462 
464  retval[0] = f[Order];
465  for (size_t i(Order); i>0; i--) {
466  for (size_t j = Order-(i-1); j>0; j--)
467  retval[j] = retval[j] * t + retval[j-1];
468  retval[0] = retval[0] * t + f[i-1];
469  }
470  return retval;
471  }
472 
479  template<size_t Order, class Coeff_t, class PolyVar>
482  retval[0] = f[Order];
483  for (size_t i(Order); i>0; i--) {
484  for (size_t j = Order-(i-1); j>0; j--)
485  retval[j] += retval[j-1];
486  retval[0] += f[i-1];
487  }
488  return retval;
489  }
490 
529  template<size_t Order, class Coeff_t, class PolyVar>
532  retval[0] = f[0];
533  for (size_t i(Order); i>0; i--) {
534  for (size_t j = Order-(i-1); j>0; j--)
535  retval[j] += retval[j-1];
536  retval[0] += f[Order - (i-1)];
537  }
538  return retval;
539  }
540 
543  template<size_t Order, class Coeff_t, class PolyVar>
546  for (size_t i(1); i <= Order; i+=2)
547  g[i] = -g[i];
548  return g;
549  }
550 
554  template<size_t Order, class Coeff_t, class PolyVar>
557  Coeff_t factor = 1;
558  for (size_t i(1); i <= Order; ++i)
559  g[i] *= (factor *= a);
560  return g;
561  }
578  template<class Coeff_t, size_t Order, class Coeff_t2, class PolyVar>
579  Coeff_t precision(const Polynomial<Order, Coeff_t, PolyVar>& f, const Coeff_t2& x)
580  {
581  if (std::isinf(x))
582  return 0.0;
583 
584  if (Order == 0)
585  return 0;
586 
587  static_assert(std::is_floating_point<Coeff_t>::value, "This has only been implemented for floating point types");
588  static_assert(std::numeric_limits<Coeff_t>::radix == 2, "This has only been implemented for base-2 floating point types");
589  const int mantissa_digits = std::numeric_limits<Coeff_t>::digits;
590  const Coeff_t eps = 1.06 / std::pow(2, mantissa_digits);
591 
592  Coeff_t sum = f[0];
593  Coeff_t2 xn = std::abs(x);
594  for(size_t i = 1; i <= Order; ++i) {
595  sum += (2 * i + 1) * std::abs(f[i]) * xn;
596  xn *= std::abs(x);
597  }
598 
599  return sum * eps;
600  }
601 
602 
659  template<size_t Order, class Coeff_t, class PolyVar>
660  inline Polynomial<Order-1, Coeff_t, PolyVar> deflate_polynomial(const Polynomial<Order, Coeff_t, PolyVar>& a, const double root) {
661  if (root == Coeff_t())
662  return deflate_polynomial(a, Null());
663 
664  Polynomial<Order-1, Coeff_t, PolyVar> b;
665  //Calculate the highest and lowest order coefficients using
666  //these stable approaches
667  b[Order-1] = a[Order];
668  b[0] = - a[0] / root;
669 
670  size_t i_t = Order-2;
671  size_t i_b = 1;
672  while (i_t >= i_b) {
673  const Coeff_t d = root * b[i_t + 1];
674  if (subtraction_precision(b[i_b], a[i_b]) > addition_precision(a[i_t+1], d)) {
675  b[i_b] = (b[i_b-1] - a[i_b]) / root;
676  ++i_b;
677  } else {
678  b[i_t] = a[i_t+1] + d;
679  --i_t;
680  }
681  }
682  return b;
683  }
684 
692  template<size_t Order, class Coeff_t, class PolyVar>
693  inline Polynomial<Order-1, Coeff_t, PolyVar> deflate_polynomial(const Polynomial<Order, Coeff_t, PolyVar>& a, Null) {
694  Polynomial<Order-1, Coeff_t, PolyVar> b;
695  std::copy(a.begin()+1, a.end(), b.begin());
696  return b;
697  }
698 
704  template<class PolyVar>
706  return StackVector<double, 0>();
707  }
708 
714  template<class PolyVar>
717  if (f[1] != 0)
718  roots.push_back(-f[0] / f[1]);
719  return roots;
720  }
721 
729  template<class PolyVar>
731  //If this is actually a linear polynomial, drop down to that solver.
732  if (f[2] == 0)
733  return solve_real_roots(change_order<1>(f));
734 
735  if (f[0] == 0)
736  //There is no constant term, so we actually have x^2 + f[1] * x = 0
737  return StackVector<double, 2>{std::min(-f[1], 0.0), std::max(-f[1], 0.0)};
738 
739  //Scale the constant of x^2 to 1
740  f = expand(f / f[2]);
741 
742  static const double maxSqrt = std::sqrt(std::numeric_limits<double>::max());
743  if (std::abs(f[1]) > maxSqrt) {
744  //arg contains f[1]*f[1], so it will overflow.
745  StackVector<double, 2> retval{-f[1], -f[0] / f[1]};
746  //Sort them into order
747  if (retval[0] > retval[1])
748  std::swap(retval[0], retval[1]);
749  return retval;
750  }
751 
752  const double arg = f[1] * f[1] - 4 * f[0];
753 
754  //Test if there are no real roots
755  if (arg < 0)
756  return StackVector<double, 2>();
757 
758  //Test if there is a double root
759  if (arg == 0)
760  return StackVector<double, 2>{-f[1] * 0.5};
761 
762  //Return both roots
763  const double root1 = -(f[1] + std::copysign(std::sqrt(arg), f[1])) * 0.5;
764  const double root2 = f[0] / root1;
765  StackVector<double, 2> retval{root1, root2};
766  if (root1 > root2)
767  std::swap(retval[0], retval[1]);
768  return retval;
769  }
770 
771  namespace {
779  template<size_t Order, class Coeff_t, class PolyVar>
780  inline StackVector<Coeff_t, Order> deflate_and_solve_polynomial(const Polynomial<Order, Coeff_t, PolyVar>& f, Coeff_t root) {
782  //The roots obtained through deflation are not the roots of
783  //the original equation. They will need polishing using the
784  //original function:
785  for (auto& root : roots)
786  //We don't test if the polishing fails. Without established
787  //bounds polishing is hard to do for multiple roots, so we
788  //just accept a polished root if it is available
789  stator::numeric::halleys_method([&](double x){ return eval_derivatives<2>(f, x); }, root);
790 
791  roots.push_back(root);
792  std::sort(roots.begin(), roots.end());
793  return roots;
794  }
795  }
796 
804  template<class PolyVar>
806  //Ensure this is actually a third order polynomial
807  if (f_original[3] == 0)
808  return solve_real_roots(change_order<2>(f_original));
809 
810  if (f_original[0] == 0)
811  //If the constant is zero, one root is x=0. We can divide
812  //by x and solve the remaining quadratic
813  return deflate_and_solve_polynomial(f_original, 0.0);
814 
815  //Convert to a cubic with a unity high-order coefficient
816  auto f = expand(f_original / f_original[3]);
817 
818  if ((f[2] == 0) && (f[1] == 0))
819  //Special case where f(x) = x^3 + f[0]
820  return StackVector<double, 3>{std::cbrt(-f[0])};
821 
822  static const double maxSqrt = std::sqrt(std::numeric_limits<double>::max());
823 
824  if ((f[2] > maxSqrt) || (f[2] < -maxSqrt))
825  //The equation is limiting to x^3 + f[2] * x^2 == 0. Use
826  //this to estimate the location of one root, polish it up,
827  //then deflate the polynomial and solve the quadratic.
828  return deflate_and_solve_polynomial(f, -f[2]);
829 
830  //NOT SURE THESE RANGE TESTS ARE BENEFICIAL
831  if (f[1] > maxSqrt)
832  //Special case, if f[1] is large (and f[2] is not) the root is
833  //near -f[0] / f[1], the x^3 term is negligble, and all other terms
834  //cancel.
835  return deflate_and_solve_polynomial(f, -f[0] / f[1]);
836 
837  if (f[1] < -maxSqrt)
838  //Special case, equation is approximated as x^3 + q x == 0
839  return deflate_and_solve_polynomial(f, -std::sqrt(-f[1]));
840 
841  if ((f[0] > maxSqrt) || (f[0] < -maxSqrt))
842  //Another special case where equation is approximated as f(x)= x^3 +f[0]
843  return deflate_and_solve_polynomial(f, -std::cbrt(f[0]));
844 
845  const double v = f[0] + (2.0 * f[2] * f[2] / 9.0 - f[1]) * (f[2] / 3.0);
846 
847  if ((v > maxSqrt) || (v < -maxSqrt))
848  return deflate_and_solve_polynomial(f, -f[2]);
849 
850  const double uo3 = f[1] / 3.0 - f[2] * f[2] / 9.0;
851  const double u2o3 = uo3 + uo3;
852 
853  if ((u2o3 > maxSqrt) || (u2o3 < -maxSqrt))
854  {
855  if (f[2]==0)
856  {
857  if (f[1] > 0)
858  return deflate_and_solve_polynomial(f, -f[0] / f[1]);
859 
860  if (f[1] < 0)
861  return deflate_and_solve_polynomial(f, -std::sqrt(-f[1]));
862 
863  return deflate_and_solve_polynomial(f, 0.0);
864  }
865 
866  return deflate_and_solve_polynomial(f, -f[1] / f[2]);
867  }
868 
869  const double uo3sq4 = u2o3 * u2o3;
870  if (uo3sq4 > maxSqrt)
871  {
872  if (f[2] == 0)
873  {
874  if (f[1] > 0)
875  return deflate_and_solve_polynomial(f, -f[0] / f[1]);
876 
877  if (f[1] < 0)
878  return deflate_and_solve_polynomial(f, -std::sqrt(-f[1]));
879 
880  return deflate_and_solve_polynomial(f, 0.0);
881  }
882 
883  return deflate_and_solve_polynomial(f, -f[1] / f[2]);
884  }
885 
886  const double j = (uo3sq4 * uo3) + v * v;
887 
888  if (j > 0)
889  {//Only one root (but this test can be wrong due to a
890  //catastrophic cancellation in j) (i.e. (uo3sq4 * uo3) == v
891  //* v) so we solve for this root then solve the deflated
892  //quadratic.
893 
894  const double w = std::sqrt(j);
895  double root;
896  if (v < 0)
897  root = std::cbrt(0.5*(w-v)) - (uo3) * std::cbrt(2.0 / (w-v)) - f[2] / 3.0;
898  else
899  root = uo3 * std::cbrt(2.0 / (w+v)) - std::cbrt(0.5*(w+v)) - f[2] / 3.0;
900 
901  //We don't test if the polishing fails. Without established
902  //bounds polishing is hard to do for multiple roots, so we
903  //just accept a polished root if it is available
904  stator::numeric::halleys_method([&](double x){ return eval_derivatives<2>(f, x); }, root);
905 
906  return deflate_and_solve_polynomial(f, root);
907  }
908 
909  if (uo3 >= 0)
910  //Triple root detected
911  return StackVector<double, 3>{std::cbrt(v) - f[2] / 3.0};
912 
913  const double muo3 = - uo3;
914  double s = 0;
915  if (muo3 > 0)
916  {
917  s = std::sqrt(muo3);
918  if (f[2] > 0) s = -s;
919  }
920 
921  const double scube = s * muo3;
922  if (scube == 0)
923  return StackVector<double, 3>{ -f[2] / 3.0 };
924 
925  double t = - v / (scube + scube);
926  //Clamp t, as in certain edge cases it might numerically fall
927  //outside of the valid range of acos
928  t = std::min(std::max(t, -1.0), +1.0);
929  const double k = std::acos(t) / 3.0;
930  const double cosk = std::cos(k);
931 
932  StackVector<double, 3> roots{ (s + s) * cosk - f[2] / 3.0 };
933 
934  const double sinsqk = 1.0 - cosk * cosk;
935  if (sinsqk < 0)
936  return roots;
937 
938  double rt3sink = std::sqrt(3.0) * std::sqrt(sinsqk);
939  roots.push_back(s * (-cosk + rt3sink) - f[2] / 3.0);
940  roots.push_back(s * (-cosk - rt3sink) - f[2] / 3.0);
941 
942  //We don't test if the polishing fails. Without established
943  //bounds polishing is "hard" with multiple real roots, so we
944  //just accept a polished root if it is available
945  stator::numeric::halleys_method([&](double x){ return eval_derivatives<2>(f, x); }, roots[0]);
946  stator::numeric::halleys_method([&](double x){ return eval_derivatives<2>(f, x); }, roots[1]);
947  stator::numeric::halleys_method([&](double x){ return eval_derivatives<2>(f, x); }, roots[2]);
948 
949  std::sort(roots.begin(), roots.end());
950  return roots;
951  }
952 
953  namespace detail {
957  template<size_t Order, class Coeff_t, class PolyVar>
958  Polynomial<Order-2, Coeff_t, PolyVar>
960  {
961  Polynomial<Order-2, Coeff_t, PolyVar> rem;
962  std::tie(std::ignore, rem) = gcd(f, g);
963  return -rem;
964  }
965 
968  template<size_t Order, class Coeff_t, class PolyVar>
969  struct SturmChain {
974  _p_n(p_n), _p_nminus1(p_n, derivative(p_n, PolyVar()))
975  {}
976 
981  _p_n(p_n), _p_nminus1(p_n, mrem(p_nplus1, p_n))
982  {}
983 
985  SturmChain<Order-1, Coeff_t, PolyVar> _p_nminus1;
986 
995  if (i == 0)
996  return _p_n;
997  else
998  return _p_nminus1.get(i-1);
999  }
1000 
1007  template<class Coeff_t2>
1008  size_t sign_changes(const Coeff_t2& x) const {
1009  return sign_change_helper(0, x);
1010  }
1011 
1012  template<class Coeff_t2>
1013  size_t roots(const Coeff_t2& a, const Coeff_t2& b) const {
1014  return std::abs(int(sign_changes(a)) - int(sign_changes(b)));
1015  }
1016 
1017 
1028  template<class Coeff_t2>
1029  size_t sign_change_helper(const int last_sign, const Coeff_t2& x) const {
1030  const Coeff_t currentx = sub(_p_n, PolyVar() = x);
1031  const int current_sign = (currentx != 0) * (1 - 2 * std::signbit(currentx));
1032  const bool sign_change = (current_sign != 0) && (last_sign != 0) && (current_sign != last_sign);
1033 
1034  const int next_sign = (current_sign != 0) ? current_sign : last_sign;
1035  return _p_nminus1.sign_change_helper(next_sign, x) + sign_change;
1036  }
1037 
1038  void output_helper(std::ostream& os, const size_t max_order) const {
1039  os << ",\n p_" << max_order - Order << "=" << _p_n;
1040  _p_nminus1.output_helper(os, max_order);
1041  }
1042  };
1043 
1047  template<class Coeff_t, class PolyVar>
1048  struct SturmChain<0, Coeff_t, PolyVar> {
1053  _p_n(p_n)
1054  {}
1055 
1060  _p_n(p_n) {}
1061 
1062  Polynomial<0, Coeff_t, PolyVar> get(size_t i) const {
1063  if (i == 0)
1064  return _p_n;
1066  }
1067 
1068  template<class Coeff_t2>
1069  size_t sign_changes(const Coeff_t2& x) const {
1070  return 0;
1071  }
1072 
1074 
1075  template<class Coeff_t2>
1076  size_t sign_change_helper(const int last_sign, const Coeff_t2& x) const {
1077  const Coeff_t currentx = sub(_p_n, PolyVar() = x);
1078  const int current_sign = (currentx != 0) * (1 - 2 * std::signbit(currentx));
1079  const bool sign_change = (current_sign != 0) && (last_sign != 0) && (current_sign != last_sign);
1080  return sign_change;
1081  }
1082 
1083  void output_helper(std::ostream& os, const size_t max_order) const {
1084  os << ",\n p_" << max_order << "=" << _p_n;
1085  }
1086  };
1087  }
1088 
1145  template<size_t Order, class Coeff_t, class PolyVar>
1148  }
1149 
1160  template<size_t Order, class Coeff_t, class PolyVar>
1162  //Count the sign changes
1163  size_t sign_changes(0);
1164  int last_sign = 0;
1165  for (size_t i(0); i <= Order; ++i) {
1166  const int current_sign = (f[i] != 0) * (1 - 2 * std::signbit(f[i]));
1167  sign_changes += (current_sign != 0) && (last_sign != 0) && (current_sign != last_sign);
1168  last_sign = (current_sign != 0) ? current_sign : last_sign;
1169  }
1170  return sign_changes;
1171  }
1172 
1197  template<size_t Order, class Coeff_t, class PolyVar>
1200  }
1201 
1212  template<size_t Order, class Coeff_t, class PolyVar>
1213  size_t alesina_galuzzi_test(const Polynomial<Order, Coeff_t, PolyVar>& f, const Coeff_t& a, const Coeff_t& b) {
1214  return budan_01_test(scale_poly(shift_function(f, a), b - a));
1215  }
1216 
1226  template<class Coeff_t, size_t Order, class PolyVar>
1228  std::array<size_t, Order+1> times_used;
1229  times_used.fill(1);
1230  Coeff_t ub = Coeff_t();
1231 
1232  size_t real_order = Order;
1233  while ((real_order > 0) && (f[real_order] == 0))
1234  --real_order;
1235 
1236  for (int m(real_order-1); m >= 0; --m)
1237  if (std::signbit(f[m]) != std::signbit(f[real_order])) {
1238  Coeff_t tempub = std::numeric_limits<Coeff_t>::infinity();
1239  for (int k(real_order); k > m; --k)
1240  if (std::signbit(f[k]) != std::signbit(f[m]))
1241  {
1242  Coeff_t temp = std::pow(-(1 << times_used[k]) * f[m] / f[k], 1.0 / (k - m));
1243  ++times_used[k];
1244  tempub = std::min(temp, tempub);
1245  }
1246  ub = std::max(tempub, ub);
1247  }
1248  return ub;
1249  }
1250 
1268  template<class Coeff_t, size_t Order, class PolyVar>
1270  return 1.0 / LMQ_upper_bound(Polynomial<Order, Coeff_t, PolyVar>(f.rbegin(), f.rend()));
1271  }
1272 
1278  template<class Coeff_t, class PolyVar>
1280  return 0;
1281  }
1282 
1288  template<class Coeff_t, class PolyVar>
1290  return HUGE_VAL;
1291  }
1292 
1300  template<size_t Order, class Coeff_t, class PolyVar>
1303  //Test how many roots are in the range (0,1)
1304  switch (budan_01_test(f)) {
1305  case 0:
1306  //No roots, return empty
1307  return StackVector<std::pair<Coeff_t,Coeff_t>, Order>();
1308  case 1:
1309  //One root! the bound is (0,1)
1310  return StackVector<std::pair<Coeff_t,Coeff_t>, Order>{std::make_pair(Coeff_t(0), Coeff_t(1))};
1311  default:
1312  //Possibly multiple roots, so divide the range [0,1] in half
1313  //and recursively explore it.
1314 
1315  //We first scale the polynomial so that the original range
1316  //x=[0,1] is scaled to the range x=[0,2]. To do this, we
1317  //perform the subsitution x->x/2. Rather than use division
1318  //(even though a division by 2 usually cheap), we actually
1319  //generate this function \f$p1(x) = 2^{Order}\,f(x/2)\f$. This
1320  //transforms this to a multiplication op which is always cheap.
1322  for (size_t i(0); i <= Order; ++i)
1323  p1[i] *= (1 << (Order-i)); //This gives (2^Order) / (2^i)
1324 
1325  //Perform a Taylor shift p2(x) = p1(x+1). This gives
1326  //
1327  //p2(x) = 2^Order f(x/2 + 0.5)
1328  //
1329  //in terms of the original function, f(x).
1331 
1332  //Now that we have two scaled and shifted polynomials where
1333  //the roots in f in the range x=[0,0.5] are in p1 over the
1334  //range \f$x=[0,1]\f$, and the roots of f in the range
1335  //x=[0.5,1.0] are in p2 over the range \f$x=[0,1]\f$. Search
1336  //them both and combine the results.
1337 
1338  auto retval = VCA_real_root_bounds_worker(p1);
1339  //Scale these roots back to the original mapping
1340  for (auto& root_bound : retval) {
1341  root_bound.first /= 2;
1342  root_bound.second /= 2;
1343  }
1344 
1345  auto second_range = VCA_real_root_bounds_worker(p2);
1346  //Scale these roots back to the original mapping
1347  for (auto& root_bound : second_range)
1348  retval.push_back(std::make_pair(root_bound.first / 2 + 0.5, root_bound.second / 2 + 0.5));
1349 
1350  return retval;
1351  }
1352  }
1353 
1363  template<size_t Order, class Coeff_t, class PolyVar>
1364  StackVector<std::pair<Coeff_t,Coeff_t>, Order>
1366  //Calculate the upper bound on the polynomial real roots, and
1367  //return if no roots are detected
1368  const Coeff_t upper_bound = LMQ_upper_bound(f);
1369  if (upper_bound == 0)
1370  return StackVector<std::pair<Coeff_t,Coeff_t>, Order>();
1371 
1372  //Scale the polynomial so that all roots lie in the region
1373  //(0,1), then solve for its roots
1374  auto bounds = VCA_real_root_bounds_worker(scale_poly(f, upper_bound) );
1375 
1376  //finally, we must undo the scaling on the roots found
1377  for (auto& bound : bounds) {
1378  bound.first *= upper_bound;
1379  bound.second *= upper_bound;
1380  }
1381 
1382  return bounds;
1383  }
1384 
1401  template<class Coeff_t>
1402  struct MobiusTransform: public std::array<Coeff_t, 4> {
1403  typedef std::array<Coeff_t, 4> Base;
1406  MobiusTransform(Coeff_t a, Coeff_t b, Coeff_t c, Coeff_t d):
1407  Base{{a,b,c,d}}
1408  {}
1409 
1413  Coeff_t eval(Coeff_t x) {
1414  //Catch the special case that there is no x term
1415  if (((*this)[0] == 0) && ((*this)[2] == 0))
1416  return (*this)[1] / (*this)[3];
1417 
1418  //Special case of inf/inf
1419  if (std::isinf(x) && ((*this)[0] != 0) && ((*this)[2] != 0))
1420  return (*this)[0] / (*this)[2];
1421 
1422  //We have to be careful not to do 0 * inf.
1423  Coeff_t numerator = (*this)[1];
1424  if ((*this)[0] != 0)
1425  numerator += x * (*this)[0];
1426 
1427  Coeff_t denominator = (*this)[3];
1428  if ((*this)[2] != 0)
1429  denominator += x * (*this)[2];
1430 
1431  return numerator / denominator;
1432  }
1433 
1437  void shift(Coeff_t x) {
1438  (*this)[1] += (*this)[0] * x;
1439  (*this)[3] += (*this)[2] * x;
1440  }
1441 
1445  void scale(Coeff_t x) {
1446  (*this)[0] *= x;
1447  (*this)[2] *= x;
1448  }
1449 
1454  Base::operator=(std::array<Coeff_t, 4>{{(*this)[1], (*this)[0] + (*this)[1], (*this)[3], (*this)[2] + (*this)[3]}});
1455  }
1456  };
1457 
1469  template<size_t Order, class Coeff_t, class PolyVar>
1470  StackVector<std::pair<Coeff_t,Coeff_t>, Order>
1472  //This while loop is only used to allow restarting the method
1473  //without recursion, as this may recurse a large number of
1474  //times.
1475  while (true) {
1476  //Test how many positive roots exist
1477  const size_t sign_changes = descartes_rule_of_signs(f);
1478 
1479  if (sign_changes == 0)
1480  //No roots, return empty
1481  return StackVector<std::pair<Coeff_t,Coeff_t>, Order>();
1482 
1483  if (sign_changes == 1)
1484  //One root! the bounds are M(0) and M(\infty)
1485  return StackVector<std::pair<Coeff_t,Coeff_t>, Order>{std::make_pair(M.eval(0), M.eval(HUGE_VAL))};
1486 
1487  auto lb = LMQ_lower_bound(f);
1488 
1489  //Attempt to divide the polynomial range from [0, \infty] up
1490  //into [0, 1] and [1, \infty].
1491 
1492  //If there's a large jump in the lower bound, then this will
1493  //take too long to converge. Try rescaling the polynomial. The
1494  //factor 16 is empirically selected.
1495  if (lb >= 16) {
1496  f = scale_poly(f, lb);
1497  M.scale(lb);
1498  lb = 1;
1499  }
1500 
1501  //Check if the lower bound suggests that this split is a waste
1502  //of time (no roots in [0,1]), if so, shift the polynomial and
1503  //try again. This also occurs if there was a large jump in the
1504  //lower bound as detected above.
1505  if (lb >= 1) {
1506  f = shift_function(f, lb);
1507  M.shift(lb);
1508  continue; //Start again
1509  }
1510 
1511  if (std::abs(sub(f, PolyVar() = 1.0)) <= (100 * precision(f, 1.0))) {
1512  //There is probably a root near x=1.0 as its approached zero
1513  //closely (compared to the precision of the polynomial
1514  //evaluation). Rather than trying to divide it out or do
1515  //anything too smart, just scale the polynomial so that next
1516  //time it falls at x=0.5.
1517  //
1518  //This is needed as we are using finite precision math
1519  //(floating point)
1520  const Coeff_t scale = 2.0;
1521  f = scale_poly(f, scale);
1522  M.scale(scale);
1523  continue; //Start again
1524  }
1525 
1526  //Check for a root at x=0. If there is one, divide it out!
1527  StackVector<std::pair<Coeff_t,Coeff_t>, Order> retval;
1528  if (f[0] == 0) {
1529  retval.push_back(std::make_pair(M.eval(0), M.eval(0)));
1530  f = deflate_polynomial(f, Null());
1531  }
1532 
1533  //Create and solve the polynomial for [0, 1]
1535  auto M01 = M;
1536  M01.invert_taylor_shift();
1537  auto first_range = VAS_real_root_bounds_worker(p01, M01);
1538  for (const auto& bound: first_range)
1539  retval.push_back(bound);
1540 
1541  //Create and solve the polynomial for [1, \infty]
1543  auto M1inf = M;
1544  M1inf.shift(1);
1545  auto second_range = VAS_real_root_bounds_worker(p1inf, M1inf);
1546  for (const auto& bound: second_range)
1547  retval.push_back(bound);
1548 
1549  return retval;
1550  }
1551  }
1552 
1553  template<class Coeff_t, class PolyVar>
1554  StackVector<std::pair<Coeff_t,Coeff_t>, 1>
1556  return StackVector<std::pair<Coeff_t,Coeff_t>, 1>();
1557  }
1558 
1567  template<size_t Order, class Coeff_t, class PolyVar>
1568  StackVector<std::pair<Coeff_t,Coeff_t>, Order>
1570  //Calculate the upper bound on the polynomial real roots, and
1571  //return if no roots are detected
1572  const Coeff_t upper_bound = LMQ_upper_bound(f);
1573  if (upper_bound == 0)
1574  return StackVector<std::pair<Coeff_t,Coeff_t>, Order>();
1575 
1576  auto bounds = VAS_real_root_bounds_worker(f, MobiusTransform<Coeff_t>(1,0,0,1));
1577  for (auto& bound: bounds) {
1578  //Sort the bound values correctly
1579  if (bound.first > bound.second)
1580  std::swap(bound.first, bound.second);
1581  //Replace infinite bounds with the upper bound estimate
1582  if (std::isinf(bound.second))
1583  bound.second = upper_bound;
1584  }
1585 
1586  return bounds;
1587  }
1588 
1592  enum class PolyRootBounder {
1593  VCA, VAS, STURM
1594  };
1595 
1598  enum class PolyRootBisector {
1599  BISECTION
1600  };
1601 
1610  template<size_t Order, class Coeff_t, class PolyVar1, class PolyVar2 = PolyVar1,
1611  typename = typename std::enable_if<(Order > 2)>::type,
1612  typename = typename enable_if_var_in<PolyVar1, PolyVar2>::type>
1615  typedef typename variable_combine<PolyVar1, PolyVar2>::type NewPolyVar;
1616 
1617  if (f[Order] == Coeff_t())
1618  return LinBairstowSolve(change_order<Order-1>(f), tolerance);
1619 
1620  Eigen::Matrix<Coeff_t, 2, 1> dGuess{0, 0};
1621  //Now loop solving for the quadratic guess
1622  for (size_t it(0); it < 20; ++it) {
1625  //Determine the error (the actual remainder)
1626  std::tie(p1, rem1) = gcd(f, guess);
1627 
1628  //Also determine the gradient of the error with respect to the
1629  //lowest order coefficients of the guess. These are related
1630  //to the remainder of a second division
1631  std::tie(std::ignore, rem2) = gcd(p1, guess);
1632 
1633  //Perform a Newton-Raphson step in the remainder, while varying the guess
1634  Eigen::Matrix<Coeff_t, 2, 1> F;
1635  F << rem1[0] , rem1[1]; //{S, R}
1636  Eigen::Matrix<Coeff_t, 2, 2> J;
1637  J << -rem2[0], guess[0] * rem2[1], -rem2[1], guess[1] * rem2[1] - rem2[0]; //{dS/dC, dS/dB, dR/dC, dR/dB}
1638 
1639  //New guess is calculated
1640  dGuess = J.inverse() * (-F);
1641  guess[0] = guess[0] + dGuess[0];
1642  guess[1] = guess[1] + dGuess[1];
1643  //std::cout << guess << " 2 " << dGuess.squaredNorm() << std::endl;
1644  if (dGuess.squaredNorm() <= tolerance*tolerance)
1645  return guess;
1646  }
1647 
1648  throw std::runtime_error("Iteration count exceeded");
1649  }
1650 
1657  template<class Coeff_t, class PolyVar1, class PolyVar2 = PolyVar1, size_t Order,
1658  typename = typename std::enable_if<(Order <= 2)>::type,
1659  typename = typename enable_if_var_in<PolyVar1, PolyVar2>::type>
1662  return f;
1663  }
1664 
1669  template<class Coeff_t, size_t Order, class PolyVar>
1672  //Establish bounds on the positive roots
1673  const Coeff_t max = LMQ_upper_bound(f);
1674  const Coeff_t min = LMQ_lower_bound(f);
1675 
1676  //If there are no roots, then return
1677  if (min > max) return StackVector<Coeff_t, Order>();
1678 
1679  //Construct the Sturm chain and bisect it
1680  auto chain = sturm_chain(f);
1681 
1682  StackVector<std::tuple<Coeff_t,Coeff_t,size_t>, Order> regions{std::make_tuple(min, max, chain.roots(min, max))};
1684 
1685  const Coeff_t eps = std::max(Coeff_t(std::ldexp(1.0, 1-tol_bits)), Coeff_t(2*std::numeric_limits<Coeff_t>::epsilon()));
1686 
1687  bool try_bisection = true;
1688 
1689  auto f_bisection = [&](Coeff_t x) { return sub(f, PolyVar() = x); };
1690 
1691  while(!regions.empty()) {
1692  auto range = regions.pop_back();
1693  const Coeff_t xmin = std::get<0>(range);
1694  const Coeff_t xmax = std::get<1>(range);
1695  const size_t roots = std::get<2>(range);
1696 
1697  Coeff_t xmid = (xmin + xmax) / 2;
1698 
1699  //Check if we have reached the tolerance of the calculations via Sturm bisection
1700  if (std::abs(xmin - xmax) <= (eps * std::min(std::abs(xmin), std::abs(xmax)))) {
1701  for (size_t i(0); i < roots; ++i)
1702  retval.push_back(xmid);
1703  try_bisection = true;
1704  continue;
1705  }
1706 
1707  size_t rootsa = chain.roots(xmin, xmid);
1708  size_t rootsb = chain.roots(xmid, xmax);
1709 
1710  if ((rootsa + rootsb) != roots) {
1711  //A precision error of the calculations has caused us to
1712  //lose track of the roots. This may be caused by us dropping
1713  //xmid exactly on a root.
1714 
1715  //Try shifting where we bisected the range
1716  xmid = (xmid + xmax) / 2;
1717  rootsa = chain.roots(xmin, xmid);
1718  rootsb = chain.roots(xmid, xmax);
1719  if ((rootsa + rootsb) != roots) {
1720  //That didn't work. Rather than abort, assume this is a
1721  //precision error and there are roots somewhere in
1722  //xmin/xmax. Return this as a best estimate
1723  retval.push_back((xmin + xmax) / 2);
1724  continue;
1725  }
1726  }
1727 
1728  if (rootsa > 1)
1729  regions.push_back(std::make_tuple(xmin, xmid, rootsa));
1730  else if (rootsa == 1) {
1731  if (try_bisection) {
1732  Coeff_t root;
1733  bool result = stator::numeric::bisection(f_bisection, root, xmin, xmid);
1734  if (result)
1735  retval.push_back(root);
1736  else
1737  regions.push_back(std::make_tuple(xmin, xmid, rootsa));
1738  }
1739  else
1740  regions.push_back(std::make_tuple(xmin, xmid, rootsa));
1741  }
1742  if (rootsb > 1)
1743  regions.push_back(std::make_tuple(xmid, xmax, rootsb));
1744  else if (rootsb == 1) {
1745  if (try_bisection) {
1746  Coeff_t root;
1747  bool result = stator::numeric::bisection(f_bisection, root, xmid, xmax);
1748  if (result)
1749  retval.push_back(root);
1750  else
1751  regions.push_back(std::make_tuple(xmid, xmax, rootsb));
1752  }
1753  else
1754  regions.push_back(std::make_tuple(xmid, xmax, rootsa));
1755  }
1756  }
1757 
1758  return retval;
1759  }
1760 
1767  template<PolyRootBounder BoundMode, PolyRootBisector BisectionMode, size_t Order, class Coeff_t, class PolyVar>
1770  StackVector<std::pair<Coeff_t,Coeff_t>, Order> bounds;
1771 
1772  switch (BoundMode) {
1773  case PolyRootBounder::STURM: return solve_real_positive_roots_poly_sturm(f); break;
1774  case PolyRootBounder::VCA: bounds = VCA_real_root_bounds(f); break;
1775  case PolyRootBounder::VAS: bounds = VAS_real_root_bounds(f); break;
1776  }
1777 
1778  //Now bisect to calculate the roots to full precision
1780 
1781  auto f_bisection = [&](Coeff_t x) { return sub(f, PolyVar() = x); };
1782  for (const auto& bound : bounds) {
1783  const Coeff_t& a = bound.first;
1784  const Coeff_t& b = bound.second;
1785  switch(BisectionMode) {
1786  case PolyRootBisector::BISECTION:
1787  {
1788  Coeff_t root;
1789  bool result = stator::numeric::bisection(f_bisection, root, a, b);
1790  if (!result)
1791  stator_throw() << "Bisection failed! Impossibru!";
1792  retval.push_back(root);
1793  break;
1794  }
1795  }
1796  }
1797 
1798  std::sort(retval.begin(), retval.end());
1799  return retval;
1800  }
1801 
1802  /*\brief Solve for the distinct real roots of a Polynomial.
1803 
1804  This is a general implementation of the polynomial real root
1805  solver. It defaults to using the VAS algorithm, and polishing up
1806  the roots using the TOMS748 algorithm. It will recurse and call
1807  the default root solver if the polynomial has a zero
1808  leading-order coefficient!
1809 
1810  Roots are always returned sorted lowest-first.
1811  */
1812  template<PolyRootBounder BoundMode = PolyRootBounder::STURM, PolyRootBisector BisectionMode = PolyRootBisector::BISECTION, size_t Order, class Coeff_t, class PolyVar>
1815  //Handle special cases
1816  //The constant coefficient is zero: deflate the polynomial
1817  if (f[0] == Coeff_t())
1819 
1820  //The highest order coefficient is zero: drop to lower order
1821  //solvers
1822  if (f[Order] == Coeff_t())
1823  return solve_real_roots(change_order<Order-1>(f));
1824 
1825  auto roots = solve_real_positive_roots_poly<BoundMode, BisectionMode, Order, Coeff_t, PolyVar>(f);
1826  auto neg_roots = solve_real_positive_roots_poly<BoundMode, BisectionMode, Order, Coeff_t, PolyVar>(reflect_poly(f));
1827 
1828  //We need to flip the sign on the negative roots
1829  for (const auto& root: neg_roots)
1830  roots.push_back(-root);
1831 
1832  std::sort(roots.begin(), roots.end());
1833  return roots;
1834  }
1835 
1836  namespace detail {
1837  template<size_t Order, class Coeff_t, class PolyVar>
1838  std::ostream& operator<<(std::ostream& os, const SturmChain<Order, Coeff_t, PolyVar>& c) {
1839  os << "SturmChain{p_0=" << c._p_n;
1840  c._p_nminus1.output_helper(os, Order);
1841  os << "}";
1842  return os;
1843  }
1844  }
1845 } // namespace sym
size_t budan_01_test(const Polynomial< Order, Coeff_t, PolyVar > &f)
Budan&#39;s upper bound estimate for the number of real roots in a Polynomial over the range ...
size_t sign_change_helper(const int last_sign, const Coeff_t2 &x) const
StackVector< double, 0 > solve_real_roots(const Polynomial< 0, double, PolyVar > &f)
Specialisation for no roots of a 0th order Polynomial.
Definition: polynomial.hpp:705
const Arg & _val
Definition: symbolic.hpp:80
detail::SturmChain< Order, Coeff_t, PolyVar > sturm_chain(const Polynomial< Order, Coeff_t, PolyVar > &f)
Helper function to generate a SturmChain from a Polynomial.
std::array< Coeff_t, 4 > Base
Coeff_t LMQ_lower_bound(const Polynomial< 0, Coeff_t, PolyVar > &f)
Specialisation of Local-max Quadratic lower-bound estimation for real roots of a Polynomial, where the Polynomial is a constant.
Polynomial< 2, Coeff_t, PolyVar1 > LinBairstowSolve(Polynomial< Order, Coeff_t, PolyVar1 > f, Coeff_t tolerance, Polynomial< 2, Coeff_t, PolyVar2 > guess={0, 0, 1})
Solve for a quadratic factor of the passed Polynomial using the LinBairstow method.
Polynomial(std::initializer_list< Coeff_t > _list)
List initializer for simple Polynomial construction.
Definition: polynomial.hpp:86
StackVector< std::pair< Coeff_t, Coeff_t >, Order > VAS_real_root_bounds(const Polynomial< Order, Coeff_t, PolyVar > &f)
Calculate bounds on all of the positive real roots of a Polynomial.
Symbolic representation of a variable.
Definition: symbolic.hpp:94
C< 0 > Null
A symbolic representation of zero.
Definition: constants.hpp:51
Coeff_t precision(const Polynomial< Order, Coeff_t, PolyVar > &f, const Coeff_t2 &x)
Calculate a maximum error estimate for the evaluation of the polynomial at .
Definition: polynomial.hpp:579
auto N(const T &a) -> STATOR_AUTORETURN(simplify< NConfig >(a))
A variant of simplify that converts compile time symbols into numbers.
size_t alesina_galuzzi_test(const Polynomial< Order, Coeff_t, PolyVar > &f, const Coeff_t &a, const Coeff_t &b)
Alesina-Galuzzi upper bound estimate for the numer of real roots in a Polynomial over a specified ran...
Base::iterator end()
Returns an iterator pointing to the end of the container.
void output_helper(std::ostream &os, const size_t max_order) const
std::tuple< Polynomial< Order1, Coeff_t, typename variable_combine< PolyVar1, PolyVar2 >::type >, Polynomial< Order2 - 1, Coeff_t, typename variable_combine< PolyVar1, PolyVar2 >::type > > gcd(const Polynomial< Order1, Coeff_t, PolyVar1 > &f, const Polynomial< Order2, Coeff_t, PolyVar2 > &g)
Perform Euclidean division of a polynomial.
Definition: polynomial.hpp:340
Polynomial< Order, Coeff_t, Var< VarArgs... > > sub(const Polynomial< Order, Coeff_t, Var1 > &f, const Relation< Var2, Var< VarArgs... > > &x_container)
Optimised Polynomial substitution which performs an exchange of the Polynomial Var.
Definition: polynomial.hpp:181
constexpr Polynomial< Order, Coeff_t, PolyVar > empty_sum(const Polynomial< Order, Coeff_t, PolyVar > &)
Returns the empty sum of a Polynomial.
Definition: polynomial.hpp:159
size_t sign_changes(const Coeff_t2 &x) const
Polynomial operator-() const
Unary negative operator to change the sign of a Polynomial.
Definition: polynomial.hpp:126
Coeff_t LMQ_lower_bound(const Polynomial< Order, Coeff_t, PolyVar > &f)
Local-max Quadratic lower bound estimate for the real roots of a Polynomial.
Polynomial< Order, Coeff_t, PolyVar > scale_poly(const Polynomial< Order, Coeff_t, PolyVar > &f, const Coeff_t &a)
Returns a polynomial where is the scaling factor.
Definition: polynomial.hpp:555
auto cos(const C< num, den > &a) -> STATOR_AUTORETURN(cos_Cimpl(a, detail::select_overload
Definition: constants.hpp:186
size_t descartes_rule_of_signs(const Polynomial< Order, Coeff_t, PolyVar > &f)
Calculates an upper bound estimate for the number of positive real roots of a Polynomial (including m...
bool bisection(const F &f, Real &x, Real low_bound, Real high_bound, size_t iterations=0, int digits=std::numeric_limits< Real >::digits/2)
Definition: numeric.hpp:272
Polynomial< Order, double, PolyVar > shift_function(const Polynomial< Order, Coeff_t, PolyVar > &f, const double t)
Returns a polynomial .
Definition: polynomial.hpp:458
Polynomial< N-1, Coeff_t, typename variable_combine< PolyVar, Var< VarArgs... > >::type > derivative(const Polynomial< N, Coeff_t, PolyVar > &f, Var< VarArgs... >)
Derivatives of Polynomial types.
Definition: polynomial.hpp:402
Fundamental typedef&#39;s and macros for stator.
Stack-allocated equivalent of std::vector.
Polynomial< Order, Coeff_t, PolyVar > _p_n
Definition: polynomial.hpp:984
Worker class for symbolically evaluating a substitution on a Polynomial.
Definition: polynomial.hpp:250
A class representing a compile-time rational constant (i.e., std::ratio).
Definition: constants.hpp:31
Coeff_t sub(const Polynomial< Order, Coeff_t, PolyVar > &f, const Relation< SubVar, Null > &)
Optimised Polynomial substitution for Null insertions.
Definition: polynomial.hpp:188
StackVector< Coeff_t, Order > solve_real_positive_roots_poly(const Polynomial< Order, Coeff_t, PolyVar > &f)
Iterative solver for the real roots of a square-free Polynomial.
size_t roots(const Coeff_t2 &a, const Coeff_t2 &b) const
auto expand(const T &a) -> STATOR_AUTORETURN(simplify< ExpandConfig >(a))
A variant of simplify that expands into Polynomial types aggressively.
SturmChain(const Polynomial< 0, Coeff_t, PolyVar > &p_n)
Constructor is the first and last Polynomial in the chain.
Polynomial< 0, Coeff_t, PolyVar > _p_n
SturmChain< Order-1, Coeff_t, PolyVar > _p_nminus1
Definition: polynomial.hpp:985
StackVector< std::pair< Coeff_t, Coeff_t >, Order > VCA_real_root_bounds_worker(const Polynomial< Order, Coeff_t, PolyVar > &f)
Calculate interval bounds on all of the positive real roots between of a squarefree Polynomial...
StackVector< double, 3 > solve_real_roots(const Polynomial< 3, double, PolyVar > &f_original)
Calculate the real roots of a 3rd order Polynomial using radicals.
Definition: polynomial.hpp:805
Polynomial(const Polynomial< N, Coeff_t2, PolyVar2 > &poly)
Constructor for constructing higher-order Polynomial types from lower order Polynomial types...
Definition: polynomial.hpp:117
Coeff_t eval(Coeff_t x)
Evaluate the Mobius transformation at the transformed .
std::tuple< Polynomial< Order1, Coeff_t, typename variable_combine< PolyVar1, PolyVar2 >::type >, Polynomial< 0, Coeff_t, typename variable_combine< PolyVar1, PolyVar2 >::type > > gcd(const Polynomial< Order1, Coeff_t, PolyVar1 > &f, const Polynomial< 0, Coeff_t, PolyVar2 > &g)
Specialisation for division by a constant.
Definition: polynomial.hpp:375
Class representing the current Mobius transformation applied to a Polynomial.
Polynomial< Order-2, Coeff_t, PolyVar > mrem(const Polynomial< Order, Coeff_t, PolyVar > &f, const Polynomial< Order-1, Coeff_t, PolyVar > &g)
Calculates the negative of the remainder of the division of by .
Definition: polynomial.hpp:959
void push_back(const T &val)
Add an element to the end of the container.
Polynomial< NewOrder, Coeff_t, PolyVar > change_order(const Polynomial< Order, Coeff_t, PolyVar > &f)
Change the order of a Polynomial.
Definition: polynomial.hpp:140
C< 1 > Unity
A symbolic representation of one.
Definition: constants.hpp:53
Polynomial< 2, Coeff_t, typename variable_combine< PolyVar1, PolyVar2 >::type > LinBairstowSolve(Polynomial< Order, Coeff_t, PolyVar1 > f, Coeff_t tolerance, Polynomial< 2, Coeff_t, PolyVar2 > guess={0, 0, 1})
Solve for a quadratic factor of the passed Polynomial using the LinBairstow method.
PolyRootBounder
Enumeration of the types of root bounding methods we have for solve_real_roots.
Polynomial< Order, Coeff_t, PolyVar > shift_function(const Polynomial< Order, Coeff_t, PolyVar > &f, Unity)
Returns a polynomial .
Definition: polynomial.hpp:480
Symbolic representation of a variable substitution.
Definition: symbolic.hpp:76
bool halleys_method(const F &f, Real &x, Real low_bound=-HUGE_VAL, Real high_bound=+HUGE_VAL, size_t iterations=0, int digits=std::numeric_limits< Real >::digits/2)
Safeguarded Halley&#39;s method for detecting a root.
Definition: numeric.hpp:240
Coeff_t LMQ_upper_bound(const Polynomial< Order, Coeff_t, PolyVar > &f)
Local-max Quadratic upper bound estimate for the value of the real roots of a Polynomial.
Null derivative(const Polynomial< N, Coeff_t, PolyVar > &f, Var< VarArgs... >)
Derivatives of Polynomial types.
Definition: polynomial.hpp:417
Polynomial< Order-1, Coeff_t, PolyVar > deflate_polynomial(const Polynomial< Order, Coeff_t, PolyVar > &a, Null)
A specialisation of deflation where the root is at zero.
Definition: polynomial.hpp:693
auto sub(const Polynomial< Order, Coeff_t, PolyVar > &f, const Relation< SubVar, Coeff_t2 > &x_container) -> STATOR_AUTORETURN(detail::PolySubWorker< Order >::eval(f, x_container._val)) template< size_t D, size_t Order, class Coeff_t, class PolyVar, class Coeff_t2 > std::array< Coeff_t, D+1 > eval_derivatives(const Polynomial< Order, Coeff_t, PolyVar > &f, const Coeff_t2 &x)
Symbolically evaluates a Polynomial expression.
Definition: polynomial.hpp:285
#define STATOR_AUTORETURN(EXPR)
A convenience Macro for defining auto return type functions.
Definition: config.hpp:51
The stator symbolic math library.
SturmChain(const Polynomial< Order, Coeff_t, PolyVar > &p_n)
Constructor if is the first Polynomial in the chain.
Definition: polynomial.hpp:973
StackVector< double, 2 > solve_real_roots(Polynomial< 2, double, PolyVar > f)
Calcualte the real roots of a 2nd order Polynomial using radicals.
Definition: polynomial.hpp:730
size_t sign_change_helper(const int last_sign, const Coeff_t2 &x) const
Helper function for calculating the sign changes in the Sturm chain.
PolyRootBisector
Enumeration of the types of bisection routines we have for solve_real_roots .
StackVector< Coeff_t, Order > solve_real_positive_roots_poly_sturm(const Polynomial< Order, Coeff_t, PolyVar > &f, const size_t tol_bits=56)
Determine the positive real roots of a polynomial using bisection and Sturm chains.
size_t addition_precision(const T f1, const T f2)
Calculate a "precision" for addition between two float types.
Definition: precision.hpp:69
constexpr size_t max_order(size_t N, size_t M)
Definition: simplify.hpp:340
Polynomial< Order, Coeff_t, PolyVar > invert_taylor_shift(const Polynomial< Order, Coeff_t, PolyVar > &f)
Returns a polynomial .
Definition: polynomial.hpp:530
constexpr C<(1 - 2 *(num< 0)) *num,(1 - 2 *(den< 0)) *den > abs(const C< num, den > &a)
Definition: constants.hpp:189
void shift(Coeff_t x)
Add the effect of a shift_poly operation to the Mobius transform.
auto pow(const LHS &l, const RHS &r) -> STATOR_AUTORETURN(std::pow(l, r))
#define stator_throw()
Definition: exception.hpp:26
void output_helper(std::ostream &os, const size_t max_order) const
void scale(Coeff_t x)
Add the effect of a scale_poly operation to the Mobius transform.
A type trait to denote symbolic terms (i.e., one that is not yet immediately evaluable to a "normal" ...
Definition: symbolic.hpp:50
Polynomial< Order-1, Coeff_t, PolyVar > deflate_polynomial(const Polynomial< Order, Coeff_t, PolyVar > &a, const double root)
Factors out a root of a polynomial and returns a lower-order polynomial with the remaining roots...
Definition: polynomial.hpp:660
Polynomial< Order, Coeff_t, PolyVar > reflect_poly(const Polynomial< Order, Coeff_t, PolyVar > &f)
Returns a polynomial .
Definition: polynomial.hpp:544
Polynomial()
Default constructor.
Definition: polynomial.hpp:74
size_t sign_changes(const Coeff_t2 &x) const
Coeff_t LMQ_upper_bound(const Polynomial< 0, Coeff_t, PolyVar > &f)
Specialisation of Local-max Quadratic upper-bound estimation for real roots of a Polynomial, where the Polynomial is a constant.
SturmChain(const Polynomial< Order+1, Coeff_t, PolyVar > &p_nplus1, const Polynomial< Order, Coeff_t, PolyVar > &p_n)
Constructor if is an intermediate Polynomial in the chain.
Definition: polynomial.hpp:980
Polynomial(InputIt first, InputIt last)
Definition: polynomial.hpp:99
StackVector< std::pair< Coeff_t, Coeff_t >, Order > VCA_real_root_bounds(const Polynomial< Order, Coeff_t, PolyVar > &f)
Calculate bounds on all of the positive real roots of a Polynomial.
StackVector< double, 1 > solve_real_roots(const Polynomial< 1, double, PolyVar > &f)
Calculate the single real root (if it exists) of a 1st order Polynomial.
Definition: polynomial.hpp:715
MobiusTransform(Coeff_t a, Coeff_t b, Coeff_t c, Coeff_t d)
Constructor for the Mobius transformation.
size_t subtraction_precision(const T f1, const T f2)
Calculate a "precision" for subtraction between two float types.
Definition: precision.hpp:47
SturmChain(const Polynomial< 1, Coeff_t, PolyVar > &p_nplus1, const Polynomial< 0, Coeff_t, PolyVar > &p_n)
Constructor if this is the last Polynomial in the chain.
StackVector< std::pair< Coeff_t, Coeff_t >, Order > VAS_real_root_bounds_worker(Polynomial< Order, Coeff_t, PolyVar > f, MobiusTransform< Coeff_t > M)
Calculate bounds on all of the positive real roots in the range of a Polynomial.
void invert_taylor_shift()
Add the effect of a scale_poly operation to the Mobius transform.
auto store(const T &val) -> decltype(store_impl(val, select_overload
Definition: config.hpp:94
A collection of Polynomials which form a Sturm chain.
Definition: polynomial.hpp:969
Array representation of Polynomial.
Definition: polynomial.hpp:63