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