Sturm  0.0.0
Computing Sturm sequences in C++
Loading...
Searching...
No Matches
Poly.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2026, Davide Stocco and Enrico Bertolazzi. *
3 * *
4 * The Sturm project is distributed under the BSD 2-Clause License. *
5 * *
6 * Davide Stocco Enrico Bertolazzi *
7 * University of Trento University of Trento *
8 * davide.stocco@unitn.it enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef INCLUDE_STURM_POLY_HH
14#define INCLUDE_STURM_POLY_HH
15
16#include "Sturm.hh"
17
18namespace Sturm {
19
27 template <typename Real>
28 class Poly : public Eigen::Vector<Real, Eigen::Dynamic> {
29 public:
30 using Vector =
31 Eigen::Vector<Real, Eigen::Dynamic>;
32 constexpr static const Real EPSILON{
33 std::numeric_limits<Real>::epsilon()};
34
35 private:
37
43 return *static_cast<Vector *>(this);
44 }
45
46 public:
50 Poly() : m_order(0) {}
51
57 this->resize(order);
58 this->setZero();
59 }
60
65 Poly(const Poly &c) : Vector(c), m_order(c.m_order) {}
66
71 explicit Poly(const Vector &c) {
72 this->resize(c.size());
73 this->Vector::operator=(c); // to avoid recursion
74 this->m_order = Integer(c.size());
75 }
76
81 const Vector &to_eigen() const {
82 return *static_cast<const Vector *>(this);
83 }
84
90 this->m_order = order;
91 this->resize(order);
92 this->setZero();
93 }
94
100 this->m_order = degree + 1;
101 this->resize(this->m_order);
102 this->setZero();
103 }
104
110 Poly &set_scalar(Real s) {
111 this->resize(1);
112 this->coeffRef(0) = s;
113 this->m_order = 1;
114 return *this;
115 }
116
123 this->resize(2);
124 this->coeffRef(0) = a;
125 this->coeffRef(1) = 1;
126 this->m_order = 2;
127 return *this;
128 }
129
134 Vector coeffs() const {
135 return this->to_eigen();
136 }
137
142 Integer degree() const {
143 return this->m_order - 1;
144 }
145
150 Integer order() const {
151 return this->m_order;
152 }
153
158 std::string to_string() const {
159 if (this->order() <= 0) {
160 return "(empty polynomial)";
161 }
162 if (this->order() == 1) {
163 return std::to_string(this->coeff(0));
164 };
165 if ((*this).cwiseAbs().maxCoeff() == 0) {
166 return "0";
167 };
168
169 bool empty{true}; // true means that all the coefficients are zero
170 std::string s{""}; // sign
171 Real c{0}; // coefficient
172 std::string e{""}; // exponent
173 std::string res{""};
174
175 // Check if the first coefficient is different from zero
176 if (this->coeff(0) != 0) {
177 res = std::to_string(this->coeff(0));
178 empty = false;
179 }
180
181 for (Integer i{1}; i < this->order(); ++i) {
182 // If the coefficient is negative...
183 if (this->coeff(i) < 0) {
184 // and if the previous coefficients are null...
185 if (empty) {
186 s = ""; // ...do not write the sign
187 c = this->coeff(i);
188 empty = false;
189 } else {
190 s = " - "; // ...otherwise write the sign as a separator
191 c = -this->coeff(i); // ...and change the sign of the coefficient
192 }
193
194 } else if (this->coeff(i) > 0) {
195 // If the coefficient is positive...
196 c = this->coeff(i);
197 // and if the previous coefficients are null...
198 if (empty) {
199 s = ""; // ...do not write the sign
200 empty = false;
201 } else {
202 s = " + "; // ...otherwise write the sign as a separator
203 }
204
205 } else {
206 // If the coefficient is zero, skip to the next
207 continue;
208 }
209
210 // If the exponent is 1, do not write it
211 if (i == 1) {
212 e = "x";
213 } else {
214 e = "x^" + std::to_string(i);
215 }
216
217 // If the coefficient is 1, do not write it
218 if (c == 1) {
219 res += s;
220 res += e;
221 } else {
222 res += s + std::to_string(c) + e;
223 }
224 }
225 return res;
226 }
227
233 Real evaluate(Real x) const {
234 Integer n{this->m_order - 1};
235 Real res{this->coeff(n)};
236 while (n-- > 0) {
237 res = res * x + this->coeff(n);
238 }
239 return res;
240 }
241
247 Real evaluate_derivative(Real x) const {
248 Integer n{this->m_order - 1};
249 Real Dp{this->coeff(n) * n};
250 while (--n > 0) {
251 Dp = Dp * x + this->coeff(n) * n;
252 }
253 return Dp;
254 }
255
262 void evaluate(Real x, Real &p, Real &Dp) const {
263 Integer n{this->m_order - 1};
264 p = this->coeff(n);
265 Dp = this->coeff(n) * n;
266 while (--n > 0) {
267 p = p * x + this->coeff(n);
268 Dp = Dp * x + this->coeff(n) * n;
269 }
270 p = p * x + this->coeff(0);
271 }
272
277 Real leading_coeff() const {
278 return this->coeff(m_order - 1);
279 }
280
285 void derivative(Poly &res) const {
286 res.resize(this->m_order - 1);
287 for (Integer i{1}; i < this->m_order; ++i) {
288 res.coeffRef(i - 1) = i * this->coeff(i);
289 }
290 res.m_order = this->m_order - 1;
291 }
292
297 void integral(Poly &res) const {
298 res.resize(this->m_order + 1);
299 res.coeffRef(0) = 0;
300 for (Integer i{1}; i <= this->m_order; ++i) {
301 res.coeffRef(i) = this->coeff(i - 1) / i;
302 }
303 res.m_order = this->m_order + 1;
304 }
305
311 void integral(Poly &res, Real c) const {
312 res.resize(this->m_order + 1);
313 res.coeffRef(0) = c;
314 for (Integer i{1}; i <= this->m_order; ++i) {
315 res.coeffRef(i) = this->coeff(i - 1) / i;
316 }
317 res.m_order = this->m_order + 1;
318 }
319
326 Real normalize() {
327 Real scale{this->cwiseAbs().maxCoeff()};
328 if (scale > static_cast<Real>(0.0)) {
329 this->to_eigen() /= scale;
330 }
331 return scale;
332 }
333
341 void purge(const Real eps) {
342 if (this->m_order > 0) {
343 Real max{this->cwiseAbs().maxCoeff()};
344 if (max < 1) {
345 max = 1;
346 }
347 Real eps_tmp{eps * max};
348 for (Integer i{0}; i < this->m_order; ++i) {
349 Real &c_i{this->coeffRef(i)};
350 if (std::abs(c_i) <= eps_tmp) {
351 c_i = 0;
352 }
353 }
354 }
355 this->adjust_degree();
356 }
357
365 while (this->m_order > 0 && this->coeff(this->m_order - 1) == 0) {
366 --this->m_order;
367 }
368 this->conservativeResize(this->m_order);
369 }
370
378 Integer sign_var{0};
379 Integer last_sign{0};
380 for (Integer i{0}; i < this->m_order; ++i) {
381 Real v = this->coeff(i);
382 if (v > 0) {
383 if (last_sign == -1) {
384 ++sign_var;
385 }
386 last_sign = 1;
387 } else if (v < 0) {
388 if (last_sign == 1) {
389 ++sign_var;
390 }
391 last_sign = -1;
392 }
393 }
394 return sign_var;
395 }
396
407 void make_monic() {
408 this->to_eigen() /= this->coeff(this->m_order - 1);
409 this->coeffRef(this->m_order - 1) = 1;
410 }
411
417 Poly &operator=(const Poly &p) {
418 this->resize(p.m_order);
419 this->to_eigen().noalias() = p.to_eigen();
420 this->m_order = p.m_order;
421 return *this;
422 }
423
429 return Poly(-this->to_eigen());
430 }
431
437 Poly &operator+=(const Poly &p) {
438 Integer max_order{std::max(this->m_order, p.m_order)};
439 Integer min_order{std::min(this->m_order, p.m_order)};
440 this->conservativeResize(
441 max_order); // Resize the vector without destroying the content
442 this->head(min_order).noalias() += p.head(min_order);
443 Integer n_tail{p.m_order - this->m_order};
444 if (n_tail > 0) {
445 this->tail(n_tail).noalias() = p.tail(n_tail);
446 }
447 this->m_order = max_order;
448 return *this;
449 }
450
456 Poly &operator-=(const Poly &p) {
457 Integer max_order{std::max(this->m_order, p.m_order)};
458 Integer min_order{std::min(this->m_order, p.m_order)};
459 this->conservativeResize(
460 max_order); // Resize the vector without destroying the content
461 this->head(min_order).noalias() -= p.head(min_order);
462 Integer n_tail{p.m_order - this->m_order};
463 if (n_tail > 0) {
464 this->tail(n_tail).noalias() = -p.tail(n_tail);
465 }
466 this->m_order = max_order;
467 return *this;
468 }
469
475 Poly &operator*=(const Poly &p) {
476 Vector a(this->to_eigen());
477 Integer new_order{this->m_order + p.m_order - 1};
478 this->resize(new_order);
479 this->setZero();
480 for (Integer i{0}; i < this->m_order; ++i) {
481 for (Integer j{0}; j < p.m_order; ++j) {
482 this->coeffRef(i + j) += a.coeff(i) * p.coeff(j);
483 }
484 }
485 this->m_order = new_order;
486 return *this;
487 }
488
494 Poly &operator+=(Real s) {
495 if (this->m_order > 0)
496 this->coeffRef(0) += s;
497 else {
498 this->resize(1);
499 this->coeffRef(0) = s;
500 this->m_order = 1;
501 }
502 return *this;
503 }
504
510 Poly &operator-=(Real s) {
511 if (this->m_order > 0) {
512 this->coeffRef(0) -= s;
513 } else {
514 this->resize(1);
515 this->coeffRef(0) = -s;
516 this->m_order = 1;
517 }
518 return *this;
519 }
520
526 Poly &operator*=(Real s) {
527 this->to_eigen() *= s;
528 return *this;
529 }
530
531 }; // class Poly
532
540 template <typename Real>
541 inline Poly<Real> operator+(const Poly<Real> &p_1, const Poly<Real> &p_2) {
542 Integer max_order{std::max(p_1.order(), p_2.order())};
543 Integer min_order{std::min(p_1.order(), p_2.order())};
544 Poly<Real> res(max_order);
545 res.head(min_order).noalias() = p_1.head(min_order) + p_2.head(min_order);
546 Integer n_tail{max_order - min_order};
547 if (n_tail > 0) {
548 if (p_1.order() > p_2.order()) {
549 res.tail(n_tail).noalias() = p_1.tail(n_tail);
550 } else {
551 res.tail(n_tail).noalias() = p_2.tail(n_tail);
552 }
553 }
554 return res;
555 }
556
564 template <typename Real>
565 inline Poly<Real> operator+(const Poly<Real> &p, Real s) {
566 Integer max_order{std::max(p.order(), 1)};
567 Poly<Real> res(max_order);
568 if (p.order() > 0) {
569 res.coeffRef(0) = p.coeff(0) + s;
570 if (p.order() > 1)
571 res.tail(p.order() - 1).noalias() = p.tail(p.order() - 1);
572 } else {
573 res.coeffRef(0) = s;
574 }
575 return res;
576 }
577
585 template <typename Real>
586 inline Poly<Real> operator+(Real s, const Poly<Real> &p) {
587 Integer max_order{std::max(p.order(), 1)};
588 Poly<Real> res(max_order);
589 if (p.order() > 0) {
590 res.coeffRef(0) = s + p.coeff(0);
591 if (p.order() > 1)
592 res.tail(p.order() - 1).noalias() = p.tail(p.order() - 1);
593 } else {
594 res.coeffRef(0) = s;
595 }
596 return res;
597 }
598
606 template <typename Real>
607 inline Poly<Real> operator-(const Poly<Real> &p_1, const Poly<Real> &p_2) {
608 Integer max_order{std::max(p_1.order(), p_2.order())};
609 Integer min_order{std::min(p_1.order(), p_2.order())};
610 Poly<Real> res(max_order);
611 res.head(min_order).noalias() = p_1.head(min_order) - p_2.head(min_order);
612 Integer n_tail{max_order - min_order};
613 if (n_tail > 0) {
614 if (p_1.order() > p_2.order()) {
615 res.tail(n_tail).noalias() = p_1.tail(n_tail);
616 } else {
617 res.tail(n_tail).noalias() = -p_2.tail(n_tail);
618 }
619 }
620 return res;
621 }
622
630 template <typename Real>
631 inline Poly<Real> operator-(const Poly<Real> &p, Real s) {
632 Integer max_order{std::max(p.order(), 1)};
633 Poly<Real> res(max_order);
634 if (p.order() > 0) {
635 res.coeffRef(0) = p.coeff(0) - s;
636 if (p.order() > 1)
637 res.tail(p.order() - 1).noalias() = p.tail(p.order() - 1);
638 } else {
639 res.coeffRef(0) = -s;
640 }
641 return res;
642 }
643
651 template <typename Real>
652 inline Poly<Real> operator-(Real s, const Poly<Real> &p) {
653 Integer max_order{std::max(p.order(), 1)};
654 Poly<Real> res(max_order);
655 if (p.order() > 0) {
656 res.coeffRef(0) = s - p.coeff(0);
657 if (p.order() > 1) {
658 res.tail(p.order() - 1).noalias() = -p.tail(p.order() - 1);
659 }
660 } else {
661 res.coeffRef(0) = s;
662 }
663 return res;
664 }
665
673 template <typename Real>
674 inline Poly<Real> operator*(const Poly<Real> &p_1, const Poly<Real> &p_2) {
675 Poly<Real> res(p_1.order() + p_2.order() - 1);
676 for (Integer i{0}; i < p_1.order(); ++i) {
677 for (Integer j{0}; j < p_2.order(); ++j) {
678 res.coeffRef(i + j) += p_1.coeff(i) * p_2.coeff(j);
679 }
680 }
681 return res;
682 }
683
691 template <typename Real>
692 inline Poly<Real> operator*(Real p, const Poly<Real> &s) {
693 Poly<Real> res(s.order());
694 res.noalias() = p * s.to_eigen();
695 return res;
696 }
697
705 template <typename Real>
706 inline Poly<Real> operator*(const Poly<Real> &s, Real p) {
707 Poly<Real> res(s.order());
708 res.noalias() = s.to_eigen() * p;
709 return res;
710 }
711
723 template <typename Real>
724 inline void divide(const Poly<Real> &p_1,
725 const Poly<Real> &p_2,
726 Poly<Real> &q,
727 Poly<Real> &r) {
728 // Scale polynomials as p_1_norm(x) = p_1(x) / scale_p_1, and p_2_norm(x) =
729 // p_2(x) / scale_p_2
730 Poly<Real> p_1_norm(p_1), p_2_norm(p_2);
731 Real scale_p_1{p_1_norm.normalize()};
732 Real scale_p_2{p_2_norm.normalize()};
733
734 // p_1_norm(x) = p_2_norm(x) * q(x) + r(x)
735 r = p_1_norm;
736 Real leading_b_norm{p_2_norm.leading_coeff()};
737 Integer d{r.order() - p_2_norm.order()};
738 if (d < 0) {
739 // p_1_norm(x) = p_2_norm(x) + r(x)
740 q.set_scalar(1);
741 r = p_1_norm - p_2_norm;
742 } else {
743 Integer r_degree{r.degree()};
744 q.set_order(d + 1);
745
747 std::abs(leading_b_norm) > Poly<Real>::EPSILON,
748 "Sturm::Poly::divide(...): leading coefficient of p_2(x) is 0.");
749
750 while (d >= 0 && r_degree >= 0) {
751 Real leading_r = {r(r_degree)};
752 Real ratio{leading_r / leading_b_norm};
753 q.coeffRef(d) = ratio;
754 r.segment(d, p_2_norm.degree()).noalias() -=
755 ratio * p_2_norm.head(p_2_norm.degree());
756 r.coeffRef(r_degree) = 0;
757 --r_degree;
758 --d;
759 }
760
761 // Do not purge remainder: this can be done externally with r.purge(eps);
762 r.adjust_degree();
763 }
764
765 // Scale back polinomials:
766 // - p_1_norm(x) = p_2_norm(x) * q(x) + r(x)
767 // - p_1(x) / scale_p_1 = p_2(x) / scale_p_2 * q(x) + r(x)
768 // - p_1(x) = p_2(x) * (scale_p_1/scale_p_2) * q(x) + scale_p_1*r(x)
769 q *= scale_p_1 / scale_p_2;
770 r *= scale_p_1;
771 }
772
781 template <typename Real>
782 inline void GCD(const Poly<Real> &p_1,
783 const Poly<Real> &p_2,
784 Poly<Real> &gcd,
785 Real eps = Poly<Real>::EPSILON) {
786 if (p_2.order() > 0) {
787 Poly<Real> q, r;
788 Sturm::divide(p_1, p_2, q, r);
789 r.purge(eps);
790 Sturm::GCD(p_2, r, gcd, eps);
791 } else {
792 gcd = p_1;
793 }
794 gcd.normalize();
795 }
796
804 template <typename Real>
805 inline std::ostream &operator<<(std::ostream &os, const Poly<Real> &p) {
806 os << p.to_string();
807 return os;
808 }
809
810} // namespace Sturm
811
812#endif // INCLUDE_STURM_POLY_HH
#define STURM_ASSERT(COND, MSG)
Definition Sturm.hh:38
Polynomial class.
Definition Poly.hh:28
Poly & operator-=(const Poly &p)
Define the subtraction with another polynomial.
Definition Poly.hh:456
void set_degree(Integer degree)
Set the degree of the polynomial.
Definition Poly.hh:99
Poly & operator-=(Real s)
Define the subtraction with a scalar.
Definition Poly.hh:510
Poly(const Vector &c)
Class constructor from a vector.
Definition Poly.hh:71
Poly(const Poly &c)
Class constructor from a vector.
Definition Poly.hh:65
void derivative(Poly &res) const
Compute the derivative of the polynomial.
Definition Poly.hh:285
void make_monic()
Normalize the polynomial to monic form.
Definition Poly.hh:407
static constexpr const Real EPSILON
Definition Poly.hh:32
Integer m_order
Definition Poly.hh:36
Poly & operator=(const Poly &p)
Define the assignment with another polynomial.
Definition Poly.hh:417
Poly & operator+=(Real s)
Define the addition with a scalar.
Definition Poly.hh:494
Poly operator-()
Define the negation of the polynomial.
Definition Poly.hh:428
void purge(const Real eps)
Purge small coefficients.
Definition Poly.hh:341
void adjust_degree()
Adjust the degree of the polynomial and remove leading zeros.
Definition Poly.hh:364
Vector coeffs() const
Get the coefficients of the polynomial.
Definition Poly.hh:134
void integral(Poly &res) const
Compute the integral of the polynomial.
Definition Poly.hh:297
Poly & set_monomial(Real a)
Set the polynomial to a monomial with a given coefficient.
Definition Poly.hh:122
void evaluate(Real x, Real &p, Real &Dp) const
Evaluate the polynomial and its derivative at a given point.
Definition Poly.hh:262
Poly & set_scalar(Real s)
Set the polynomial to a scalar value.
Definition Poly.hh:110
Real leading_coeff() const
Get the leading coefficient of the polynomial.
Definition Poly.hh:277
Poly(Integer order)
Class constructor from a vector.
Definition Poly.hh:56
Vector & to_eigen()
Access to the Eigen class.
Definition Poly.hh:42
Poly & operator*=(Real s)
Define the multiplication with a scalar.
Definition Poly.hh:526
Real evaluate_derivative(Real x) const
Evaluate the derivative of the polynomial at a given point.
Definition Poly.hh:247
Poly & operator*=(const Poly &p)
Define the multiplication with another polynomial.
Definition Poly.hh:475
Poly()
Class constructor from a vector.
Definition Poly.hh:50
std::string to_string() const
Convert the polynomial to a string representation.
Definition Poly.hh:158
Integer degree() const
Get the degree of the polynomial.
Definition Poly.hh:142
Real evaluate(Real x) const
Evaluate the polynomial at a given point.
Definition Poly.hh:233
Real normalize()
Normalize the polynomial.
Definition Poly.hh:326
Integer order() const
Get the order of the polynomial.
Definition Poly.hh:150
void set_order(Integer order)
Set the order of the polynomial.
Definition Poly.hh:89
Poly & operator+=(const Poly &p)
Define the addition with another polynomial.
Definition Poly.hh:437
const Vector & to_eigen() const
Access to the Eigen const class.
Definition Poly.hh:81
Eigen::Vector< Real, Eigen::Dynamic > Vector
Definition Poly.hh:30
void integral(Poly &res, Real c) const
Compute the integral of the polynomial with a given constant.
Definition Poly.hh:311
Integer sign_variations() const
Count the number of sign variations.
Definition Poly.hh:377
Namespace for the Sturm library.
Definition Sturm.hh:71
Poly< Real > operator+(const Poly< Real > &p_1, const Poly< Real > &p_2)
Define the sum between two polynomials.
Definition Poly.hh:541
void GCD(const Poly< Real > &p_1, const Poly< Real > &p_2, Poly< Real > &gcd, Real eps=Poly< Real >::EPSILON)
Compute the greatest common divisor of two polynomials.
Definition Poly.hh:782
Poly< Real > operator-(const Poly< Real > &p_1, const Poly< Real > &p_2)
Define the difference between two polynomials.
Definition Poly.hh:607
void divide(const Poly< Real > &p_1, const Poly< Real > &p_2, Poly< Real > &q, Poly< Real > &r)
Divide the polynomial.
Definition Poly.hh:724
std::ostream & operator<<(std::ostream &os, const Poly< Real > &p)
Print the polynomial on an output stream.
Definition Poly.hh:805
Poly< Real > operator*(const Poly< Real > &p_1, const Poly< Real > &p_2)
Define the multiplication between two polynomials.
Definition Poly.hh:674
STURM_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Sturm.hh:79