1 // This is core/vnl/algo/vnl_rnpoly_solve.cxx
2 //:
3 // \file
4 #include <cmath>
5 #include <cassert>
6 #include <iostream>
7 #include <vnl/algo/vnl_rnpoly_solve.h>
8 #include "vnl/vnl_math.h"
9
10 #ifdef DEBUG
11 #endif
12
13 static unsigned int dim_ = 0; // dimension of the problem
14 static unsigned int max_deg_ = 0; // maximal degree
15 static unsigned int max_nterms_ = 0; // maximal number of terms
16
17 //: This is a local implementation of a minimal "complex number" class, for internal use only
18 class vnl_rnpoly_solve_cmplx
19 {
20 public:
21 double R;
22 double C;
vnl_rnpoly_solve_cmplx(double a=0,double b=0)23 vnl_rnpoly_solve_cmplx(double a = 0, double b = 0)
24 : R(a)
25 , C(b)
26 {}
27 inline double
norm() const28 norm() const
29 {
30 return R * R + C * C;
31 }
32 inline vnl_rnpoly_solve_cmplx
operator -() const33 operator-() const
34 {
35 return { -R, -C };
36 }
37 inline vnl_rnpoly_solve_cmplx
operator +(vnl_rnpoly_solve_cmplx const & Y) const38 operator+(vnl_rnpoly_solve_cmplx const & Y) const
39 {
40 return { R + Y.R, C + Y.C };
41 }
42 inline vnl_rnpoly_solve_cmplx
operator -(vnl_rnpoly_solve_cmplx const & Y) const43 operator-(vnl_rnpoly_solve_cmplx const & Y) const
44 {
45 return { R - Y.R, C - Y.C };
46 }
47 inline vnl_rnpoly_solve_cmplx &
operator +=(vnl_rnpoly_solve_cmplx const & Y)48 operator+=(vnl_rnpoly_solve_cmplx const & Y)
49 {
50 R += Y.R;
51 C += Y.C;
52 return *this;
53 }
54 inline vnl_rnpoly_solve_cmplx &
operator -=(vnl_rnpoly_solve_cmplx const & Y)55 operator-=(vnl_rnpoly_solve_cmplx const & Y)
56 {
57 R -= Y.R;
58 C -= Y.C;
59 return *this;
60 }
operator *(vnl_rnpoly_solve_cmplx const & Y) const61 inline vnl_rnpoly_solve_cmplx operator*(vnl_rnpoly_solve_cmplx const & Y) const
62 {
63 return { R * Y.R - C * Y.C, R * Y.C + C * Y.R };
64 }
65 inline vnl_rnpoly_solve_cmplx
operator /(vnl_rnpoly_solve_cmplx const & Y) const66 operator/(vnl_rnpoly_solve_cmplx const & Y) const
67 {
68 double N = 1.0 / Y.norm();
69 return { (R * Y.R + C * Y.C) * N, (C * Y.R - R * Y.C) * N };
70 }
operator *(double T) const71 inline vnl_rnpoly_solve_cmplx operator*(double T) const { return { R * T, C * T }; }
72 inline vnl_rnpoly_solve_cmplx &
operator *=(double T)73 operator*=(double T)
74 {
75 R *= T;
76 C *= T;
77 return *this;
78 }
79 inline vnl_rnpoly_solve_cmplx &
operator *=(vnl_rnpoly_solve_cmplx const & Y)80 operator*=(vnl_rnpoly_solve_cmplx const & Y)
81 {
82 double r = R * Y.R - C * Y.C;
83 C = R * Y.C + C * Y.R;
84 R = r;
85 return *this;
86 }
87 inline vnl_rnpoly_solve_cmplx &
operator /=(vnl_rnpoly_solve_cmplx const & Y)88 operator/=(vnl_rnpoly_solve_cmplx const & Y)
89 {
90 return *this = operator/(Y);
91 }
92 };
93
94 static const double twopi = vnl_math::twopi;
95
96 static const double epsilonB = 2.e-03;
97 static const vnl_rnpoly_solve_cmplx epsilonZ = vnl_rnpoly_solve_cmplx(1.e-04, 1.e-04);
98 static const double final_eps = 1.e-10;
99 static const double stepinit = 1.e-02;
100
101
102 std::vector<vnl_vector<double> *>
realroots(double tol)103 vnl_rnpoly_solve::realroots(double tol)
104 {
105 tol *= tol; // squared tolerance
106 std::vector<vnl_vector<double> *> rr;
107 auto rp = r_.begin(), ip = i_.begin();
108 for (; rp != r_.end() && ip != i_.end(); ++rp, ++ip)
109 if ((*ip)->squared_magnitude() < tol)
110 rr.push_back(*rp);
111
112 return rr;
113 }
114
115
116 //------------------------- INPTBR ---------------------------
117 //: Initialize random variables
118 // This will initialize the random variables which are used
119 // to perturb the starting point so as to have measure zero
120 // probability that we will start at a singular point.
121 static void
inptbr(std::vector<vnl_rnpoly_solve_cmplx> & p,std::vector<vnl_rnpoly_solve_cmplx> & q)122 inptbr(std::vector<vnl_rnpoly_solve_cmplx> & p, std::vector<vnl_rnpoly_solve_cmplx> & q)
123 {
124 vnl_rnpoly_solve_cmplx pp[10], qq[10];
125
126 pp[0] = vnl_rnpoly_solve_cmplx(.12324754231, .76253746298);
127 pp[1] = vnl_rnpoly_solve_cmplx(.93857838950, -.99375892810);
128 pp[2] = vnl_rnpoly_solve_cmplx(-.23467908356, .39383930009);
129 pp[3] = vnl_rnpoly_solve_cmplx(.83542556622, -.10192888288);
130 pp[4] = vnl_rnpoly_solve_cmplx(-.55763522521, -.83729899911);
131 pp[5] = vnl_rnpoly_solve_cmplx(-.78348738738, -.10578234903);
132 pp[6] = vnl_rnpoly_solve_cmplx(.03938347346, .04825184716);
133 pp[7] = vnl_rnpoly_solve_cmplx(-.43428734331, .93836289418);
134 pp[8] = vnl_rnpoly_solve_cmplx(-.99383729993, -.40947822291);
135 pp[9] = vnl_rnpoly_solve_cmplx(.09383736736, .26459172298);
136
137 qq[0] = vnl_rnpoly_solve_cmplx(.58720452864, .01321964722);
138 qq[1] = vnl_rnpoly_solve_cmplx(.97884134700, -.14433009712);
139 qq[2] = vnl_rnpoly_solve_cmplx(.39383737289, .4154322311);
140 qq[3] = vnl_rnpoly_solve_cmplx(-.03938376373, -.61253112318);
141 qq[4] = vnl_rnpoly_solve_cmplx(.39383737388, -.26454678861);
142 qq[5] = vnl_rnpoly_solve_cmplx(-.0093837766, .34447867861);
143 qq[6] = vnl_rnpoly_solve_cmplx(-.04837366632, .48252736790);
144 qq[7] = vnl_rnpoly_solve_cmplx(.93725237347, -.54356527623);
145 qq[8] = vnl_rnpoly_solve_cmplx(.39373957747, .65573434564);
146 qq[9] = vnl_rnpoly_solve_cmplx(-.39380038371, .98903450052);
147
148 p.resize(dim_);
149 q.resize(dim_);
150 for (unsigned int j = 0; j < dim_; ++j)
151 {
152 int jj = j % 10;
153 p[j] = pp[jj];
154 q[j] = qq[jj];
155 }
156 }
157
158 //----------------------------- POWR -----------------------
159 //: This returns the complex number y raised to the nth degree
160 static inline vnl_rnpoly_solve_cmplx
powr(int n,vnl_rnpoly_solve_cmplx const & y)161 powr(int n, vnl_rnpoly_solve_cmplx const & y)
162 {
163 vnl_rnpoly_solve_cmplx x(1, 0);
164 if (n > 0)
165 while (n--)
166 x *= y;
167 else
168 while (n++)
169 x /= y;
170 return x;
171 }
172
173
174 static void
initr(std::vector<unsigned int> const & ideg,std::vector<vnl_rnpoly_solve_cmplx> const & p,std::vector<vnl_rnpoly_solve_cmplx> const & q,std::vector<vnl_rnpoly_solve_cmplx> & r,std::vector<vnl_rnpoly_solve_cmplx> & pdg,std::vector<vnl_rnpoly_solve_cmplx> & qdg)175 initr(std::vector<unsigned int> const & ideg,
176 std::vector<vnl_rnpoly_solve_cmplx> const & p,
177 std::vector<vnl_rnpoly_solve_cmplx> const & q,
178 std::vector<vnl_rnpoly_solve_cmplx> & r,
179 std::vector<vnl_rnpoly_solve_cmplx> & pdg,
180 std::vector<vnl_rnpoly_solve_cmplx> & qdg)
181 {
182 assert(ideg.size() == dim_);
183 assert(p.size() == dim_);
184 assert(q.size() == dim_);
185 pdg.resize(dim_);
186 qdg.resize(dim_);
187 r.resize(dim_);
188 for (unsigned int j = 0; j < dim_; j++)
189 {
190 pdg[j] = powr(ideg[j], p[j]);
191 qdg[j] = powr(ideg[j], q[j]);
192 r[j] = q[j] / p[j];
193 }
194 }
195
196
197 //-------------------------------- DEGREE -------------------------------
198 //: This will compute the degree of the polynomial based upon the index.
199 static inline int
degree(int index)200 degree(int index)
201 {
202 return (index < 0) ? 0 : (index % max_deg_) + 1;
203 }
204
205
206 //-------------------------- FFUNR -------------------------
207 //: Evaluate the target system component of h.
208 // This is the system of equations that we are trying to find the roots.
209 static void
ffunr(std::vector<double> const & coeff,std::vector<int> const & polyn,std::vector<unsigned int> const & terms,std::vector<vnl_rnpoly_solve_cmplx> const & x,std::vector<vnl_rnpoly_solve_cmplx> & pows,std::vector<vnl_rnpoly_solve_cmplx> & f,std::vector<vnl_rnpoly_solve_cmplx> & df)210 ffunr(std::vector<double> const & coeff,
211 std::vector<int> const & polyn,
212 std::vector<unsigned int> const & terms,
213 std::vector<vnl_rnpoly_solve_cmplx> const & x,
214 std::vector<vnl_rnpoly_solve_cmplx> & pows,
215 std::vector<vnl_rnpoly_solve_cmplx> & f,
216 std::vector<vnl_rnpoly_solve_cmplx> & df)
217 {
218 assert(terms.size() == dim_);
219 assert(x.size() == dim_);
220 // Compute all possible powers for each variable
221 pows.resize(max_deg_ * dim_);
222 for (unsigned int i = 0; i < dim_; i++) // for all variables
223 {
224 int index = max_deg_ * i;
225 pows[index] = x[i];
226 for (unsigned int j = 1; j < max_deg_; ++j, ++index) // for all powers
227 pows[index + 1] = pows[index] * x[i];
228 }
229
230 // Initialize the new arrays
231 for (unsigned int i = 0; i < dim_; ++i)
232 {
233 f[i] = vnl_rnpoly_solve_cmplx(0, 0);
234 for (unsigned int j = 0; j < dim_; j++)
235 df[i * dim_ + j] = vnl_rnpoly_solve_cmplx(0, 0);
236 }
237
238 for (unsigned int i = 0; i < dim_; ++i) // Across equations
239 for (unsigned int j = 0; j < terms[i]; ++j) // Across terms
240 {
241 vnl_rnpoly_solve_cmplx tmp(1, 0);
242 for (unsigned int k = 0; k < dim_; ++k) // For each variable
243 {
244 int index = polyn[i * dim_ * max_nterms_ + j * dim_ + k];
245 if (index >= 0)
246 tmp *= pows[index];
247 }
248 f[i] += tmp * coeff[i * max_nterms_ + j];
249 }
250
251 // Compute the Derivative!
252 for (int i = dim_ - 1; i >= 0; i--) // Over equations
253 for (int l = dim_ - 1; l >= 0; l--) // With respect to each variable
254 {
255 vnl_rnpoly_solve_cmplx & df_il = df[i * dim_ + l];
256 for (int j = terms[i] - 1; j >= 0; j--) // Over terms in each equation
257 if (polyn[i * dim_ * max_nterms_ + j * dim_ + l] >= 0) // if 0 deg in l, df term is 0
258 {
259 vnl_rnpoly_solve_cmplx tmp = vnl_rnpoly_solve_cmplx(1, 0);
260 for (int k = dim_ - 1; k >= 0; k--) // Over each variable in each term
261 {
262 int index = polyn[i * dim_ * max_nterms_ + j * dim_ + k];
263 if (index >= 0)
264 {
265 if (k == l)
266 {
267 int deg = degree(index);
268 if (deg > 1)
269 tmp *= pows[index - 1];
270 tmp *= (double)deg;
271 }
272 else
273 tmp *= pows[index];
274 }
275 } // end for k
276 df_il += tmp * coeff[i * max_nterms_ + j];
277 }
278 } // end for l
279 }
280
281
282 //--------------------------- GFUNR --------------------------
283 //: Evaluate starting system component
284 // Evaluate the starting system component of h from a system
285 // of equations that we already know the roots. (ex: x^n - 1)
286 static void
gfunr(std::vector<unsigned int> const & ideg,std::vector<vnl_rnpoly_solve_cmplx> const & pdg,std::vector<vnl_rnpoly_solve_cmplx> const & qdg,std::vector<vnl_rnpoly_solve_cmplx> const & pows,std::vector<vnl_rnpoly_solve_cmplx> & g,std::vector<vnl_rnpoly_solve_cmplx> & dg)287 gfunr(std::vector<unsigned int> const & ideg,
288 std::vector<vnl_rnpoly_solve_cmplx> const & pdg,
289 std::vector<vnl_rnpoly_solve_cmplx> const & qdg,
290 std::vector<vnl_rnpoly_solve_cmplx> const & pows,
291 std::vector<vnl_rnpoly_solve_cmplx> & g,
292 std::vector<vnl_rnpoly_solve_cmplx> & dg)
293 {
294 assert(ideg.size() == dim_);
295 assert(g.size() == dim_);
296 assert(dg.size() == dim_);
297 std::vector<vnl_rnpoly_solve_cmplx> pxdgm1(dim_), pxdg(dim_);
298
299 for (unsigned int j = 0; j < dim_; ++j)
300 {
301 vnl_rnpoly_solve_cmplx tmp;
302 if (ideg[j] <= 1)
303 tmp = vnl_rnpoly_solve_cmplx(1, 0);
304 else
305 tmp = pows[j * max_deg_ + ideg[j] - 2];
306
307 pxdgm1[j] = pdg[j] * tmp;
308 }
309
310 for (unsigned int j = 0; j < dim_; ++j)
311 {
312 int index = j * max_deg_ + ideg[j] - 1;
313 pxdg[j] = pdg[j] * pows[index];
314 }
315
316 for (unsigned int j = 0; j < dim_; ++j)
317 {
318 g[j] = pxdg[j] - qdg[j];
319 dg[j] = pxdgm1[j] * ideg[j];
320 }
321 }
322
323
324 //-------------------------- HFUNR --------------------------
325 //: This is the routine that traces the curve from the gfunr to the f function
326 // (i.e. Evaluate the continuation function)
327 static void
hfunr(std::vector<unsigned int> const & ideg,std::vector<vnl_rnpoly_solve_cmplx> const & pdg,std::vector<vnl_rnpoly_solve_cmplx> const & qdg,double t,std::vector<vnl_rnpoly_solve_cmplx> const & x,std::vector<vnl_rnpoly_solve_cmplx> & h,std::vector<vnl_rnpoly_solve_cmplx> & dhx,std::vector<vnl_rnpoly_solve_cmplx> & dht,std::vector<int> const & polyn,std::vector<double> const & coeff,std::vector<unsigned int> const & terms)328 hfunr(std::vector<unsigned int> const & ideg,
329 std::vector<vnl_rnpoly_solve_cmplx> const & pdg,
330 std::vector<vnl_rnpoly_solve_cmplx> const & qdg,
331 double t,
332 std::vector<vnl_rnpoly_solve_cmplx> const & x,
333 std::vector<vnl_rnpoly_solve_cmplx> & h,
334 std::vector<vnl_rnpoly_solve_cmplx> & dhx,
335 std::vector<vnl_rnpoly_solve_cmplx> & dht,
336 std::vector<int> const & polyn,
337 std::vector<double> const & coeff,
338 std::vector<unsigned int> const & terms)
339 {
340 assert(ideg.size() == dim_);
341 assert(terms.size() == dim_);
342 assert(x.size() == dim_);
343 assert(h.size() == dim_);
344 assert(dht.size() == dim_);
345 assert(dhx.size() == dim_ * dim_);
346 std::vector<vnl_rnpoly_solve_cmplx> df(dim_ * dim_), dg(dim_), f(dim_), g(dim_);
347 std::vector<vnl_rnpoly_solve_cmplx> pows; // powers of variables [dim_ equations] [max_deg_ possible powers]
348
349 ffunr(coeff, polyn, terms, x, pows, f, df);
350 gfunr(ideg, pdg, qdg, pows, g, dg);
351 assert(f.size() == dim_);
352 assert(g.size() == dim_);
353 assert(dg.size() == dim_);
354 assert(df.size() == dim_ * dim_);
355
356 double onemt = 1.0 - t;
357 for (unsigned int j = 0; j < dim_; ++j)
358 {
359 for (unsigned int i = 0; i < dim_; ++i)
360 dhx[j * dim_ + i] = df[j * dim_ + i] * t;
361
362 dhx[j * dim_ + j] += dg[j] * onemt;
363 dht[j] = f[j] - g[j];
364 h[j] = f[j] * t + g[j] * onemt;
365 }
366 }
367
368
369 //------------------------ LU DECOMPOSITION --------------------------
370 //: This performs LU decomposition on a matrix.
371 static int
ludcmp(std::vector<vnl_rnpoly_solve_cmplx> & a,std::vector<int> & indx)372 ludcmp(std::vector<vnl_rnpoly_solve_cmplx> & a, std::vector<int> & indx)
373 {
374 std::vector<double> vv(dim_);
375
376 // Loop over rows to get the implicit scaling information
377 for (unsigned int i = 0; i < dim_; ++i)
378 {
379 double big = 0.0;
380 for (unsigned int j = 0; j < dim_; ++j)
381 {
382 double temp = a[i * dim_ + j].norm();
383 if (temp > big)
384 big = temp;
385 }
386 if (big == 0.0)
387 return 1;
388 vv[i] = 1.0 / std::sqrt(big);
389 }
390
391 // This is the loop over columns of Crout's method
392 for (unsigned int j = 0; j < dim_; ++j)
393 {
394 for (unsigned int i = 0; i < j; ++i)
395 for (unsigned int k = 0; k < i; ++k)
396 a[i * dim_ + j] -= a[i * dim_ + k] * a[k * dim_ + j];
397
398 // Initialize for the search for largest pivot element
399 double big = 0.0;
400 unsigned int imax = 0;
401
402 for (unsigned int i = j; i < dim_; ++i)
403 {
404 for (unsigned int k = 0; k < j; ++k)
405 a[i * dim_ + j] -= a[i * dim_ + k] * a[k * dim_ + j];
406
407 // Is the figure of merit for the pivot better than the best so far?
408 double rdum = vv[i] * a[i * dim_ + j].norm();
409 if (rdum >= big)
410 {
411 big = rdum;
412 imax = i;
413 }
414 }
415
416 // Do we need to interchange rows?
417 if (j != imax)
418 {
419 // Yes, do so...
420 for (unsigned int k = 0; k < dim_; ++k)
421 {
422 vnl_rnpoly_solve_cmplx dum = a[imax * dim_ + k];
423 a[imax * dim_ + k] = a[j * dim_ + k];
424 a[j * dim_ + k] = dum;
425 }
426
427 // Also interchange the scale factor
428 vv[imax] = vv[j];
429 }
430 indx[j] = imax;
431
432 vnl_rnpoly_solve_cmplx & ajj = a[j * dim_ + j];
433 if (ajj.norm() == 0.0)
434 ajj = epsilonZ;
435
436 // Now, finally, divide by the pivot element
437 if (j + 1 != dim_)
438 {
439 vnl_rnpoly_solve_cmplx dum = vnl_rnpoly_solve_cmplx(1, 0) / ajj;
440
441 // If the pivot element is zero the matrix is singular.
442 for (unsigned int i = j + 1; i < dim_; ++i)
443 a[i * dim_ + j] *= dum;
444 }
445 }
446 return 0;
447 }
448
449
450 // ------------------------- LU Back Substitution -------------------------
451 static void
lubksb(std::vector<vnl_rnpoly_solve_cmplx> const & a,std::vector<int> const & indx,std::vector<vnl_rnpoly_solve_cmplx> const & bb,std::vector<vnl_rnpoly_solve_cmplx> & b)452 lubksb(std::vector<vnl_rnpoly_solve_cmplx> const & a,
453 std::vector<int> const & indx,
454 std::vector<vnl_rnpoly_solve_cmplx> const & bb,
455 std::vector<vnl_rnpoly_solve_cmplx> & b)
456 {
457 int ii = -1;
458 for (unsigned int k = 0; k < dim_; ++k)
459 b[k] = bb[k];
460
461 for (unsigned int i = 0; i < dim_; ++i)
462 {
463 int ip = indx[i];
464 vnl_rnpoly_solve_cmplx sum = b[ip];
465 b[ip] = b[i];
466
467 if (ii >= 0)
468 for (unsigned int j = ii; j < i; ++j)
469 sum -= a[i * dim_ + j] * b[j];
470 else
471 // A nonzero element was encountered, so from now on we
472 // will have to do the sums in the loop above
473 if (sum.norm() > 0)
474 ii = i;
475
476 b[i] = sum;
477 }
478
479 // Now do the backsubstitution
480 for (int i = dim_ - 1; i >= 0; i--)
481 {
482 for (unsigned int j = i + 1; j < dim_; ++j)
483 b[i] -= a[i * dim_ + j] * b[j];
484
485 b[i] /= a[i * dim_ + i];
486 }
487 }
488
489
490 //-------------------------- LINNR -------------------
491 //: Solve a complex system of equations by using l-u decomposition and then back substitution.
492 static int
linnr(std::vector<vnl_rnpoly_solve_cmplx> & dhx,std::vector<vnl_rnpoly_solve_cmplx> const & rhs,std::vector<vnl_rnpoly_solve_cmplx> & resid)493 linnr(std::vector<vnl_rnpoly_solve_cmplx> & dhx,
494 std::vector<vnl_rnpoly_solve_cmplx> const & rhs,
495 std::vector<vnl_rnpoly_solve_cmplx> & resid)
496 {
497 std::vector<int> irow(dim_);
498 if (ludcmp(dhx, irow) == 1)
499 return 1;
500 lubksb(dhx, irow, rhs, resid);
501 return 0;
502 }
503
504
505 //----------------------- XNORM --------------------
506 //: Finds the unit normal of a vector v
507 static double
xnorm(std::vector<vnl_rnpoly_solve_cmplx> const & v)508 xnorm(std::vector<vnl_rnpoly_solve_cmplx> const & v)
509 {
510 assert(v.size() == dim_);
511 double txnorm = 0.0;
512 for (unsigned int j = 0; j < dim_; ++j)
513 txnorm += std::fabs(v[j].R) + std::fabs(v[j].C);
514 return txnorm;
515 }
516
517 //---------------------- PREDICT ---------------------
518 //: Predict new x vector using Taylor's Expansion.
519 static void
predict(std::vector<unsigned int> const & ideg,std::vector<vnl_rnpoly_solve_cmplx> const & pdg,std::vector<vnl_rnpoly_solve_cmplx> const & qdg,double step,double & t,std::vector<vnl_rnpoly_solve_cmplx> & x,std::vector<int> const & polyn,std::vector<double> const & coeff,std::vector<unsigned int> const & terms)520 predict(std::vector<unsigned int> const & ideg,
521 std::vector<vnl_rnpoly_solve_cmplx> const & pdg,
522 std::vector<vnl_rnpoly_solve_cmplx> const & qdg,
523 double step,
524 double & t,
525 std::vector<vnl_rnpoly_solve_cmplx> & x,
526 std::vector<int> const & polyn,
527 std::vector<double> const & coeff,
528 std::vector<unsigned int> const & terms)
529 {
530 assert(ideg.size() == dim_);
531 assert(terms.size() == dim_);
532 assert(x.size() == dim_);
533
534 double maxdt = .2; // Maximum change in t for a given step. If dt is
535 // too large, there seems to be greater chance of
536 // jumping to another path. Set this to 1 if you
537 // don't care.
538 std::vector<vnl_rnpoly_solve_cmplx> dht(dim_), dhx(dim_ * dim_), dz(dim_), h(dim_), rhs(dim_);
539 // Call the continuation function that we are tracing
540 hfunr(ideg, pdg, qdg, t, x, h, dhx, dht, polyn, coeff, terms);
541
542 for (unsigned int j = 0; j < dim_; ++j)
543 rhs[j] = -dht[j];
544
545 // Call the function that solves a complex system of equations
546 if (linnr(dhx, rhs, dz) == 1)
547 return;
548
549 // Find the unit normal of a vector and normalize our step
550 double factor = step / (1 + xnorm(dz));
551 if (factor > maxdt)
552 factor = maxdt;
553
554 bool tis1 = true;
555 if (t + factor > 1)
556 {
557 tis1 = false;
558 factor = 1.0 - t;
559 }
560
561 // Update this path with the predicted next point
562 for (unsigned int j = 0; j < dim_; ++j)
563 x[j] += dz[j] * factor;
564
565 if (tis1)
566 t += factor;
567 else
568 t = 1.0;
569 }
570
571
572 //------------------------- CORRECT --------------------------
573 //: Correct the predicted point to lie near the actual curve
574 // Use Newton's Method to do this.
575 // Returns:
576 // 0: Converged
577 // 1: Singular Jacobian
578 // 2: Didn't converge in 'loop' iterations
579 // 3: If the magnitude of x > maxroot
580 static int
correct(std::vector<unsigned int> const & ideg,int loop,double eps,std::vector<vnl_rnpoly_solve_cmplx> const & pdg,std::vector<vnl_rnpoly_solve_cmplx> const & qdg,double t,std::vector<vnl_rnpoly_solve_cmplx> & x,std::vector<int> const & polyn,std::vector<double> const & coeff,std::vector<unsigned int> const & terms)581 correct(std::vector<unsigned int> const & ideg,
582 int loop,
583 double eps,
584 std::vector<vnl_rnpoly_solve_cmplx> const & pdg,
585 std::vector<vnl_rnpoly_solve_cmplx> const & qdg,
586 double t,
587 std::vector<vnl_rnpoly_solve_cmplx> & x,
588 std::vector<int> const & polyn,
589 std::vector<double> const & coeff,
590 std::vector<unsigned int> const & terms)
591 {
592 double maxroot = 1000; // Maximum size of root where it is considered heading to infinity
593 std::vector<vnl_rnpoly_solve_cmplx> dhx(dim_ * dim_), dht(dim_), h(dim_), resid(dim_);
594
595 assert(ideg.size() == dim_);
596 assert(terms.size() == dim_);
597 assert(x.size() == dim_);
598
599 for (int i = 0; i < loop; i++)
600 {
601 hfunr(ideg, pdg, qdg, t, x, h, dhx, dht, polyn, coeff, terms);
602
603 // If linnr = 1, error
604 if (linnr(dhx, h, resid) == 1)
605 return 1;
606
607 for (unsigned int j = 0; j < dim_; ++j)
608 x[j] -= resid[j];
609
610 double xresid = xnorm(resid);
611 if (xresid < eps)
612 return 0;
613 if (xresid > maxroot)
614 return 3;
615 }
616 return 2;
617 }
618
619
620 //-------------------------- TRACE ---------------------------
621 //: This is the continuation routine.
622 // It will trace a curve from a known point in the complex plane to an unknown
623 // point in the complex plane. The new end point is the root
624 // to a polynomial equation that we are trying to solve.
625 // It will return the following codes:
626 // 0: Maximum number of steps exceeded
627 // 1: Path converged
628 // 2: Step size became too small
629 // 3: Path Heading to infinity
630 // 4: Singular Jacobian on Path
631 static int
trace(std::vector<vnl_rnpoly_solve_cmplx> & x,std::vector<unsigned int> const & ideg,std::vector<vnl_rnpoly_solve_cmplx> const & pdg,std::vector<vnl_rnpoly_solve_cmplx> const & qdg,std::vector<int> const & polyn,std::vector<double> const & coeff,std::vector<unsigned int> const & terms)632 trace(std::vector<vnl_rnpoly_solve_cmplx> & x,
633 std::vector<unsigned int> const & ideg,
634 std::vector<vnl_rnpoly_solve_cmplx> const & pdg,
635 std::vector<vnl_rnpoly_solve_cmplx> const & qdg,
636 std::vector<int> const & polyn,
637 std::vector<double> const & coeff,
638 std::vector<unsigned int> const & terms)
639 {
640 assert(ideg.size() == dim_);
641 assert(terms.size() == dim_);
642 assert(x.size() == dim_);
643
644 int maxns = 500; // Maximum number of path steps
645 int maxit = 5; // Maximum number of iterations to correct a step.
646 // For each step, Newton-Raphson is used to correct
647 // the step. This should be at least 3 to improve
648 // the chances of convergence. If function is well
649 // behaved, fewer than maxit steps will be needed
650
651 double eps = 0; // epsilon value used in correct
652 double epsilonS = 1.0e-3 * epsilonB; // smallest path step for t>.95
653 double stepmin = 1.0e-5 * stepinit; // Minimum stepsize allowed
654 double step = stepinit; // stepsize
655 double t = 0.0; // Continuation parameter 0<t<1
656 double oldt = 0.0; // The previous t value
657 std::vector<vnl_rnpoly_solve_cmplx> oldx = x; // the previous path value
658 int nadv = 0;
659
660 for (int numstep = 0; numstep < maxns; numstep++)
661 {
662 // Taylor approximate the next point
663 predict(ideg, pdg, qdg, step, t, x, polyn, coeff, terms);
664
665 // if (t>1.0) t=1.0;
666
667 if (t > .95)
668 {
669 if (eps != epsilonS)
670 step = step / 4.0;
671 eps = epsilonS;
672 }
673 else
674 eps = epsilonB;
675 #ifdef DEBUG
676 std::cout << "t=" << t << std::endl;
677 #endif
678
679 if (t >= .99999) // Path converged
680 {
681 #ifdef DEBUG
682 std::cout << "path converged\n" << std::flush;
683 #endif
684 double factor = (1.0 - oldt) / (t - oldt);
685 for (unsigned int j = 0; j < dim_; ++j)
686 x[j] = oldx[j] + (x[j] - oldx[j]) * factor;
687 t = 1.0;
688 int cflag = correct(ideg, 10 * maxit, final_eps, pdg, qdg, t, x, polyn, coeff, terms);
689 if ((cflag == 0) || (cflag == 2))
690 return 1; // Final Correction converged
691 else if (cflag == 3)
692 return 3; // Heading to infinity
693 else
694 return 4; // Singular solution
695 }
696
697 // Newton's method brings us back to the curve
698 int cflag = correct(ideg, maxit, eps, pdg, qdg, t, x, polyn, coeff, terms);
699 if (cflag == 0)
700 {
701 // Successful step
702 if ((++nadv) == maxit)
703 {
704 step *= 2;
705 nadv = 0;
706 } // Increase the step size
707 // Make note of our new location
708 oldt = t;
709 oldx = x;
710 }
711 else
712 {
713 nadv = 0;
714 step /= 2.0;
715
716 if (cflag == 3)
717 return 3; // Path heading to infinity
718 if (step < stepmin)
719 return 2; // Path failed StepSizeMin exceeded
720
721 // Reset the values since we stepped to far, and try again
722 t = oldt;
723 x = oldx;
724 }
725 } // end of the loop numstep
726
727 return 0;
728 }
729
730
731 //-------------------------- STRPTR ---------------------------
732 //: This will find a starting point on the 'g' function circle.
733 // The new point to start tracing is stored in the x array.
734 static void
strptr(std::vector<unsigned int> & icount,std::vector<unsigned int> const & ideg,std::vector<vnl_rnpoly_solve_cmplx> const & r,std::vector<vnl_rnpoly_solve_cmplx> & x)735 strptr(std::vector<unsigned int> & icount,
736 std::vector<unsigned int> const & ideg,
737 std::vector<vnl_rnpoly_solve_cmplx> const & r,
738 std::vector<vnl_rnpoly_solve_cmplx> & x)
739 {
740 assert(ideg.size() == dim_);
741 assert(r.size() == dim_);
742 x.resize(dim_);
743
744 for (unsigned int i = 0; i < dim_; ++i)
745 if (icount[i] >= ideg[i])
746 icount[i] = 1;
747 else
748 {
749 icount[i]++;
750 break;
751 }
752
753 for (unsigned int j = 0; j < dim_; ++j)
754 {
755 double angle = twopi / ideg[j] * icount[j];
756 x[j] = r[j] * vnl_rnpoly_solve_cmplx(std::cos(angle), std::sin(angle));
757 }
758 }
759
760
761 static std::vector<std::vector<vnl_rnpoly_solve_cmplx>>
Perform_Distributed_Task(std::vector<unsigned int> const & ideg,std::vector<unsigned int> const & terms,std::vector<int> const & polyn,std::vector<double> const & coeff)762 Perform_Distributed_Task(std::vector<unsigned int> const & ideg,
763 std::vector<unsigned int> const & terms,
764 std::vector<int> const & polyn,
765 std::vector<double> const & coeff)
766 {
767 assert(ideg.size() == dim_);
768
769 std::vector<std::vector<vnl_rnpoly_solve_cmplx>> sols;
770 std::vector<vnl_rnpoly_solve_cmplx> pdg, qdg, p, q, r, x;
771 std::vector<unsigned int> icount(dim_, 1);
772 icount[0] = 0;
773 bool solflag; // flag used to remember if a root is found
774 #ifdef DEBUG
775 char const * FILENAM = "/tmp/cont.results";
776 std::ofstream F(FILENAM);
777 if (!F)
778 {
779 std::cerr << "could not open " << FILENAM << " for writing\nplease erase old file first\n";
780 F = std::cerr;
781 }
782 else
783 std::cerr << "Writing to " << FILENAM << '\n';
784 #endif
785 // Initialize some variables
786 inptbr(p, q);
787 initr(ideg, p, q, r, pdg, qdg);
788
789 // int Psize = 2*dim_*sizeof(double);
790 int totdegree = 1; // Total degree of the system
791 for (unsigned int j = 0; j < dim_; j++)
792 totdegree *= ideg[j];
793
794 // ************* Send initial information ****************
795 // Initialize(dim_,maxns,maxdt,maxit,maxroot,
796 // terms,ideg,pdg,qdg,coeff,polyn);
797 while ((totdegree--) > 0)
798 {
799 // Compute path to trace
800 strptr(icount, ideg, r, x);
801
802 // Tell the client which path you want it to trace
803 solflag = 1 == trace(x, ideg, pdg, qdg, polyn, coeff, terms);
804 // Save the solution for future reference
805 if (solflag)
806 {
807 #ifdef DEBUG
808 for (unsigned int i = 0; i < dim_; ++i)
809 F << '<' << x[dim_ - i - 1].R << ' ' << x[dim_ - i - 1].C << '>';
810 F << std::endl;
811 #endif
812 sols.push_back(x);
813 }
814 #ifdef DEBUG
815 // print something out for each root
816 if (solflag)
817 std::cout << '.';
818 else
819 std::cout << '*';
820 std::cout.flush();
821 #endif
822 }
823
824 #ifdef DEBUG
825 std::cout << std::endl;
826 #endif
827
828 return sols;
829 }
830
831
832 //----------------------- READ INPUT ----------------------
833 //: This will read the input polynomials from a data file.
834 void
Read_Input(std::vector<unsigned int> & ideg,std::vector<unsigned int> & terms,std::vector<int> & polyn,std::vector<double> & coeff)835 vnl_rnpoly_solve::Read_Input(std::vector<unsigned int> & ideg,
836 std::vector<unsigned int> & terms,
837 std::vector<int> & polyn,
838 std::vector<double> & coeff)
839 {
840 // Read the number of equations
841 dim_ = ps_.size();
842
843 ideg.resize(dim_);
844 terms.resize(dim_);
845 // Start reading in the array values
846 max_deg_ = 0;
847 max_nterms_ = 0;
848 for (unsigned int i = 0; i < dim_; i++)
849 {
850 ideg[i] = ps_[i]->ideg_;
851 terms[i] = ps_[i]->nterms_;
852 if (ideg[i] > max_deg_)
853 max_deg_ = ideg[i];
854 if (terms[i] > max_nterms_)
855 max_nterms_ = terms[i];
856 }
857 coeff.resize(dim_ * max_nterms_);
858 polyn.resize(dim_ * max_nterms_ * dim_);
859 for (unsigned int i = 0; i < dim_; i++)
860 {
861 for (unsigned int k = 0; k < terms[i]; k++)
862 {
863 coeff[i * max_nterms_ + k] = ps_[i]->coeffs_(k);
864 for (unsigned int j = 0; j < dim_; j++)
865 {
866 int deg = ps_[i]->polyn_(k, j);
867 polyn[i * dim_ * max_nterms_ + k * dim_ + j] = deg ? int(j * max_deg_) + deg - 1 : -1;
868 }
869 }
870 }
871 }
872
873
~vnl_rnpoly_solve()874 vnl_rnpoly_solve::~vnl_rnpoly_solve()
875 {
876 while (!r_.empty())
877 {
878 delete r_.back();
879 r_.pop_back();
880 }
881 while (!i_.empty())
882 {
883 delete i_.back();
884 i_.pop_back();
885 }
886 }
887
888 bool
compute()889 vnl_rnpoly_solve::compute()
890 {
891 std::vector<unsigned int> ideg, terms;
892 std::vector<int> polyn;
893 std::vector<double> coeff;
894
895 Read_Input(ideg, terms, polyn, coeff); // returns number of equations
896 assert(ideg.size() == dim_);
897 assert(terms.size() == dim_);
898 assert(polyn.size() == dim_ * max_nterms_ * dim_);
899 assert(coeff.size() == dim_ * max_nterms_);
900
901 int totdegree = 1;
902 for (unsigned int j = 0; j < dim_; ++j)
903 totdegree *= ideg[j];
904
905 std::vector<std::vector<vnl_rnpoly_solve_cmplx>> ans = Perform_Distributed_Task(ideg, terms, polyn, coeff);
906
907 // Print out the answers
908 vnl_vector<double> *rp, *ip;
909 #ifdef DEBUG
910 std::cout << "Total degree: " << totdegree << std::endl << "# solutions : " << ans.size() << std::endl;
911 #endif
912 for (auto & an : ans)
913 {
914 assert(an.size() == dim_);
915 rp = new vnl_vector<double>(dim_);
916 r_.push_back(rp);
917 ip = new vnl_vector<double>(dim_);
918 i_.push_back(ip);
919 for (unsigned int j = 0; j < dim_; ++j)
920 {
921 #ifdef DEBUG
922 std::cout << ans[i][j].R << " + j " << ans[i][j].C << std::endl;
923 #endif
924 (*rp)[j] = an[j].R;
925 (*ip)[j] = an[j].C;
926 }
927 #ifdef DEBUG
928 std::cout << std::endl;
929 #endif
930 }
931 return true;
932 }
933