Sturm  0.0.0
Computing Sturm sequences in C++
Loading...
Searching...
No Matches
Sequence.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_SEQUENCE_HH
14#define INCLUDE_STURM_SEQUENCE_HH
15
16#include "Sturm.hh"
17#include "Sturm/Poly.hh"
18
19namespace Sturm {
20
30 template <typename Real>
31 class Sequence {
32 public:
33 using Vector =
34 Eigen::Vector<Real, Eigen::Dynamic>;
35 using Interval = struct Interval {
36 Real a;
37 Real b;
38 Integer va;
39 Integer vb;
40 bool a_on_root;
41 bool b_on_root;
42 };
43
44 private:
45 std::vector<Poly<Real>> m_sequence;
46 std::vector<Interval> m_intervals;
47 Real m_a{0.0};
48 Real m_b{0.0};
49
50 public:
55
63 Sequence(const Poly<Real> &p) {
64 this->build(p);
65 }
66
71 Real a() const {
72 return this->m_a;
73 }
74
79 Real b() const {
80 return this->m_b;
81 }
82
86 void build(const Poly<Real> &p) {
87 this->m_intervals.clear();
88 Poly<Real> dp, q, r;
89 p.derivative(dp);
90 this->m_sequence.clear();
91 this->m_sequence.reserve(p.order());
92 this->m_sequence.emplace_back(p);
93 this->m_sequence.back().adjust_degree();
94 this->m_sequence.emplace_back(dp);
95 this->m_sequence.back().adjust_degree();
96 Integer n_sequence{1};
97 while (true) {
98 Sturm::divide<Real>(this->m_sequence[n_sequence - 1],
99 this->m_sequence[n_sequence],
100 q,
101 r);
102 if (r.order() <= 0) {
103 break;
104 }
105 this->m_sequence.emplace_back(-r);
106 ++n_sequence;
107 }
108 // Divide by GCD
109 for (Integer i{0}; i < n_sequence; ++i) {
110 Sturm::divide<Real>(this->m_sequence[i], this->m_sequence.back(), q, r);
111 q.normalize();
112 this->m_sequence[i] = q;
113 }
114 this->m_sequence.back().set_scalar(1);
115 }
116
121 Integer length() const {
122 return static_cast<Integer>(this->m_sequence.size());
123 }
124
129 const Poly<Real> &get(Integer i) const {
130 return this->m_sequence[i];
131 }
132
137 Integer sign_variations(Real x, bool &on_root) const {
138 Integer sign_var{0};
139 Integer last_sign{0};
140 Integer n_poly{Integer(this->m_sequence.size())};
141 Real v{this->m_sequence[0].evaluate(x)};
142 on_root = false;
143 if (v > 0) {
144 last_sign = 1;
145 } else if (v < 0) {
146 last_sign = -1;
147 } else {
148 on_root = true;
149 last_sign = 0;
150 }
151 for (Integer i{1}; i < n_poly; ++i) {
152 v = this->m_sequence[i].evaluate(x);
153 if (v > 0) {
154 if (last_sign == -1) {
155 ++sign_var;
156 }
157 last_sign = 1;
158 } else if (v < 0) {
159 if (last_sign == 1) {
160 ++sign_var;
161 }
162 last_sign = -1;
163 }
164 }
165 return sign_var;
166 }
167
175 Integer separate_roots(Real a_in, Real b_in) {
176 this->m_intervals.clear();
177 this->m_intervals.reserve(this->m_sequence.size());
178
179 Interval I_0, I_1;
180 this->m_a = I_0.a = a_in;
181 this->m_b = I_0.b = b_in;
182
183 I_0.va = this->sign_variations(I_0.a, I_0.a_on_root);
184 I_0.vb = this->sign_variations(I_0.b, I_0.b_on_root);
185
186 Integer n_roots{std::abs(I_0.va - I_0.vb)};
187
188 if (n_roots <= 1) {
189 if (n_roots == 1 && !I_0.a_on_root && !I_0.b_on_root) {
190 this->m_intervals.push_back(I_0);
191 }
192 if (I_0.a_on_root) {
193 I_1.a = I_1.b = I_0.a;
194 I_1.va = I_1.vb = I_0.va;
195 I_1.a_on_root = I_1.b_on_root = true;
196 this->m_intervals.push_back(I_1);
197 }
198 if (I_0.b_on_root) {
199 I_1.a = I_1.b = I_0.b;
200 I_1.va = I_1.vb = I_0.vb;
201 I_1.a_on_root = I_1.b_on_root = true;
202 this->m_intervals.push_back(I_1);
203 }
204 return static_cast<Integer>(m_intervals.size());
205 }
206
207 // Search intervals
208 std::vector<Interval> I_stack;
209 I_stack.clear();
210 I_stack.reserve(this->m_sequence.size());
211 I_stack.push_back(I_0);
212 while (I_stack.size() > 0) {
213 I_0 = I_stack.back();
214 I_stack.pop_back();
215 // Check if the interval has a single root
216 n_roots = std::abs(I_0.va - I_0.vb);
217 if (n_roots <= 1) {
218 if (I_0.a_on_root) {
219 I_0.b = I_0.a;
220 I_0.vb = I_0.va;
221 I_0.b_on_root = true;
222 this->m_intervals.push_back(I_0);
223 } else if (I_0.b_on_root) {
224 I_0.a = I_0.b;
225 I_0.va = I_0.vb;
226 I_0.a_on_root = true;
227 this->m_intervals.push_back(I_0);
228 } else if (n_roots == 1) {
229 this->m_intervals.push_back(I_0);
230 }
231 } else if (std::abs(I_0.b - I_0.a) <=
232 static_cast<Real>(10.0) *
233 std::numeric_limits<Real>::epsilon() *
234 std::max(static_cast<Real>(1.0),
235 std::max(std::abs(I_0.b), std::abs(I_0.a)))) {
236 I_1.a = I_1.b = I_0.a;
237 I_1.va = I_1.vb = 0;
238 I_1.a_on_root = I_1.b_on_root = true;
239 I_stack.push_back(I_1);
240 } else {
241 Real c{(I_0.a + I_0.b) / static_cast<Real>(2.0)};
242 bool c_on_root;
243 Integer vc{this->sign_variations(c, c_on_root)};
244 // Check the interval [a, c]
245 if (I_0.va != vc || c_on_root || I_0.a_on_root) {
246 if (c < I_0.b) { // Check if it is a true reduction
247 I_1.a = I_0.a;
248 I_1.va = I_0.va;
249 I_1.a_on_root = I_0.a_on_root;
250 I_1.b = c;
251 I_1.vb = vc;
252 I_1.b_on_root = c_on_root;
253 I_stack.push_back(I_1);
254 } else if (c_on_root) {
255 I_1.a = I_1.b = c;
256 I_1.a_on_root = I_1.b_on_root = true;
257 I_1.va = I_1.vb = 0;
258 I_stack.push_back(I_1);
259 } else if (I_0.a_on_root) {
260 I_1.a = I_1.b = I_0.a;
261 I_1.a_on_root = I_1.b_on_root = true;
262 I_1.va = I_1.vb = 0;
263 I_stack.push_back(I_1);
264 }
265 }
266 // Check the interval [c, b]
267 if (I_0.vb != vc || I_0.b_on_root) {
268 if (c > I_0.a) {
269 I_1.a = c;
270 I_1.va = vc;
271 I_1.a_on_root = c_on_root;
272 I_1.b = I_0.b;
273 I_1.vb = I_0.vb;
274 I_1.b_on_root = I_0.b_on_root;
275 I_stack.push_back(I_1);
276 } else if (I_0.b_on_root) {
277 I_1.a = I_1.b = I_0.b;
278 I_1.a_on_root = I_1.b_on_root = true;
279 I_1.va = I_1.vb = 0;
280 I_stack.push_back(I_1);
281 }
282 }
283 }
284 }
285 // Sort intervals
286 std::sort(this->m_intervals.begin(),
287 this->m_intervals.end(),
288 [](const Interval &I_a, const Interval &I_b) {
289 return I_a.a < I_b.a;
290 });
291 return static_cast<Integer>(m_intervals.size());
292 }
293
303 // Cauchy's bounds for roots
304 Real leading_coeff{this->m_sequence[0].leading_coeff()};
305 Real bound{static_cast<Real>(1.0) +
306 this->m_sequence[0].cwiseAbs().maxCoeff() /
307 std::abs(leading_coeff)};
308 return separate_roots(-bound, bound);
309 }
310
316 return static_cast<Integer>(this->m_intervals.size());
317 }
318
323 const Interval &interval(Integer i) const {
324 return this->m_intervals[i];
325 }
326
335 template <typename SolveFunction>
336 Vector refine_roots(SolveFunction &&solve_function, bool verbose = false) {
337 auto function = std::function<Real(Real x)>([this](Real x) {
338 return this->m_sequence[0].evaluate(x);
339 });
340 Vector roots(this->m_intervals.size());
341 Integer n{0};
342 bool converged{false};
343 for (auto &I : this->m_intervals) {
344 Real &r{roots.coeffRef(n++)};
345 if (I.a_on_root) {
346 r = I.a;
347 } else if (I.b_on_root) {
348 r = I.b;
349 } else {
350 converged = solve_function(I.a, I.b, function, r);
352 converged || !verbose,
353 "Sturm::Sequence::refine_roots(...): failed at interval n = "
354 << n);
355 }
356 }
357 return roots;
358 }
359
360 }; // class Sequence
361
369 template <typename Real>
370 inline std::ostream &operator<<(std::ostream &os, const Sequence<Real> &s) {
371 // Print the Sturm sequence
372 os << "Sturm sequence" << std::endl;
373 for (Integer i{0}; i < s.length(); ++i) {
374 os << "P_" << i << "(x) = " << s.get(i) << std::endl;
375 }
376 // Print the roots
377 Integer n{s.roots_number()};
378 if (n > 0) {
379 os << "roots separation for interval [" << s.a() << "," << s.b() << "]"
380 << std::endl;
381 for (Integer i{0}; i < n; ++i) {
382 const typename Sequence<Real>::Interval &I = s.interval(i);
383 os << "I = [" << I.a << ", " << I.b << "], V = [" << I.va << ", "
384 << I.vb << "]" << std::endl;
385 }
386 }
387 return os;
388 }
389
390} // namespace Sturm
391
392#endif // INCLUDE_STURM_SEQUENCE_HH
#define STURM_ASSERT_WARNING(COND, MSG)
Definition Sturm.hh:52
Polynomial class.
Definition Poly.hh:28
void derivative(Poly &res) const
Compute the derivative of the polynomial.
Definition Poly.hh:285
Real normalize()
Normalize the polynomial.
Definition Poly.hh:326
Integer order() const
Get the order of the polynomial.
Definition Poly.hh:150
Sturm sequence class.
Definition Sequence.hh:31
Integer roots_number() const
Get the number of roots found.
Definition Sequence.hh:315
struct Interval { Real a; Real b; Integer va; Integer vb; bool a_on_root; bool b_on_root; } Interval
Definition Sequence.hh:35
Integer length() const
Get the length of the stored Sturm sequence.
Definition Sequence.hh:121
Real b() const
Get the upper bound of the interval containing the roots.
Definition Sequence.hh:79
Sequence(const Poly< Real > &p)
Class constructor for the Sturm sequence given a polynomial.
Definition Sequence.hh:63
Integer separate_roots()
Compute all the subintervals containing a single root in .
Definition Sequence.hh:302
void build(const Poly< Real > &p)
Given the polynomial build its Sturm sequence.
Definition Sequence.hh:86
Integer sign_variations(Real x, bool &on_root) const
Definition Sequence.hh:137
Real m_b
Definition Sequence.hh:48
const Interval & interval(Integer i) const
Definition Sequence.hh:323
Integer separate_roots(Real a_in, Real b_in)
Compute the subintervals containing a single root.
Definition Sequence.hh:175
Real a() const
Get the lower bound of the interval containing the roots.
Definition Sequence.hh:71
const Poly< Real > & get(Integer i) const
Get the -th polynomial of the stored Sturm sequence.
Definition Sequence.hh:129
Sequence()
Class constructor for the Sturm sequence.
Definition Sequence.hh:54
std::vector< Poly< Real > > m_sequence
Definition Sequence.hh:45
Eigen::Vector< Real, Eigen::Dynamic > Vector
Definition Sequence.hh:33
Vector refine_roots(SolveFunction &&solve_function, bool verbose=false)
Compute the roots in the intervals after the separation.
Definition Sequence.hh:336
std::vector< Interval > m_intervals
Definition Sequence.hh:46
Real m_a
Definition Sequence.hh:47
Namespace for the Sturm library.
Definition Sturm.hh:71
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
STURM_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Sturm.hh:79