Rosetta
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
sturm.hh
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// @file sturm.hh
11 /// @brief implements sturm chain solver
12 /// @author Daniel J. Mandell
13 
14 #ifndef INCLUDED_numeric_kinematic_closure_sturm_hh
15 #define INCLUDED_numeric_kinematic_closure_sturm_hh
16 
17 
18 // Rosetta Headers
19 #include <numeric/types.hh>
20 
21 // C++ headers
22 #include <cmath>
23 
24 // Utility headers
25 
26 // option key includes
27 
28 
29 #include <utility/vector1_bool.hh>
30 
31 
32 // Constants
33 #define MAX_ORDER 16
34 #define MAXPOW 32
35 #define SMALL_ENOUGH 1.0e-18 // a coefficient smaller than SMALL_ENOUGH is considered to be zero (0.0)
36 #define MAX_SOLN 16 // maximum number of roots
37 
38 namespace numeric {
39 namespace kinematic_closure {
40 
41 /*
42 * structure type for representing a polynomial
43 */
44 typedef struct p {
45  int ord;
46  double coef[MAX_ORDER+1];
47 } poly;
48 
49 extern double RELERROR;
50 extern int MAXIT, MAX_ITER_SECANT;
51 
52 /* set termination criteria for polynomial solver */
53 inline
54 void initialize_sturm(double *tol_secant, int *max_iter_sturm, int *max_iter_secant)
55 {
56  RELERROR = *tol_secant;
57  MAXIT = *max_iter_sturm;
58  MAX_ITER_SECANT = *max_iter_secant;
59 }
60 
61 inline
62 double hyper_tan(double a, double x)
63 {
64  double exp_x1, exp_x2, ax;
65 
66  ax = a*x;
67  if ( ax > 100.0 ) {
68  return(1.0);
69  } else if ( ax < -100.0 ) {
70  return(-1.0);
71  } else {
72  exp_x1 = exp(ax);
73  exp_x2 = exp(-ax);
74  return (exp_x1 - exp_x2)/(exp_x1 + exp_x2);
75  }
76 }
77 
78 /*
79 * modp
80 *
81 * calculates the modulus of u(x) / v(x) leaving it in r, it
82 * returns 0 if r(x) is a constant.
83 * note: this function assumes the leading coefficient of v
84 * is 1 or -1
85 */
86 
87 inline
88 int modp(poly* u, poly* v, poly* r)
89 {
90  int k, j;
91  double *nr, *end, *uc;
92 
93  nr = r->coef;
94  end = &u->coef[u->ord];
95 
96  uc = u->coef;
97  while ( uc <= end ) {
98  *nr++ = *uc++;
99  }
100 
101  if ( v->coef[v->ord] < 0.0 ) {
102  for ( k = u->ord - v->ord - 1; k >= 0; k -= 2 ) {
103  r->coef[k] = -r->coef[k];
104  }
105  for ( k = u->ord - v->ord; k >= 0; k-- ) {
106  for ( j = v->ord + k - 1; j >= k; j-- ) {
107  r->coef[j] = -r->coef[j] - r->coef[v->ord + k] * v->coef[j - k];
108  }
109  }
110  } else {
111  for ( k = u->ord - v->ord; k >= 0; k-- ) {
112  for ( j = v->ord + k - 1; j >= k; j-- ) {
113  r->coef[j] -= r->coef[v->ord + k] * v->coef[j - k];
114  }
115  }
116  }
117 
118  k = v->ord - 1;
119  while ( k >= 0 && fabs(r->coef[k]) < SMALL_ENOUGH ) {
120  r->coef[k] = 0.0;
121  k--;
122  }
123 
124  r->ord = (k < 0) ? 0 : k;
125  return(r->ord);
126 }
127 
128 /*
129 * buildsturm
130 *
131 * build up a sturm sequence for a polynomial in smat, returning
132 * the number of polynomials in the sequence
133 */
134 
135 inline
136 int buildsturm(int ord, poly* sseq)
137 {
138  int i;
139  double f, *fp, *fc;
140  poly *sp;
141 
142  sseq[0].ord = ord;
143  sseq[1].ord = ord - 1;
144 
145  /*
146  * calculate the derivative and normalise the leading
147  * coefficient.
148  */
149  f = fabs(sseq[0].coef[ord]*ord);
150 
151  fp = sseq[1].coef;
152  fc = sseq[0].coef + 1;
153 
154  for ( i = 1; i <= ord; i++ ) {
155  *fp++ = *fc++ * i / f;
156  }
157 
158 
159  /*
160  * construct the rest of the Sturm sequence
161  */
162  for ( sp = sseq + 2; modp(sp - 2, sp - 1, sp); sp++ ) {
163 
164  /*
165  * reverse the sign and normalise
166  */
167 
168  f = -fabs(sp->coef[sp->ord]);
169  for ( fp = &sp->coef[sp->ord]; fp >= sp->coef; fp-- ) {
170  *fp /= f;
171  }
172  }
173 
174  sp->coef[0] = -sp->coef[0]; /* reverse the sign */
175 
176  return(sp - sseq);
177 }
178 
179 /*
180 * numroots
181 *
182 * return the number of distinct real roots of the polynomial
183 * described in sseq.
184 */
185 
186 inline
187 int numroots(int np, poly* sseq, int* atneg, int* atpos)
188 {
189  int atposinf, atneginf;
190  poly *s;
191  double f, lf;
192 
193  atposinf = atneginf = 0;
194 
195 
196  /*
197  * changes at positive infinity
198  */
199  lf = sseq[0].coef[sseq[0].ord];
200 
201  for ( s = sseq + 1; s <= sseq + np; s++ ) {
202  f = s->coef[s->ord];
203  if ( lf == 0.0 || lf * f < 0 ) {
204  atposinf++;
205  }
206  lf = f;
207  }
208 
209  //std::cout << "sturm.cc::numroots lf: " << lf << std::endl;
210 
211  /*
212  * changes at negative infinity
213  */
214  if ( sseq[0].ord & 1 ) {
215  lf = -sseq[0].coef[sseq[0].ord];
216  } else {
217  lf = sseq[0].coef[sseq[0].ord];
218  }
219 
220  for ( s = sseq + 1; s <= sseq + np; s++ ) {
221  if ( s->ord & 1 ) {
222  f = -s->coef[s->ord];
223  } else {
224  f = s->coef[s->ord];
225  }
226  if ( lf == 0.0 || lf * f < 0 ) {
227  atneginf++;
228  }
229  lf = f;
230  }
231 
232  //std::cout << "sturm.cc::numroots at neg inf: " << atneginf << std::endl;
233  //std::cout << "sturm.cc::numroots at pos inf: " << atposinf << std::endl;
234 
235  *atneg = atneginf;
236  *atpos = atposinf;
237  return(atneginf - atposinf);
238 }
239 
240 /*
241 * evalpoly
242 *
243 * evaluate polynomial defined in coef returning its value.
244 */
245 
246 inline
247 double evalpoly (int ord, double* coef, double x)
248 {
249  double *fp, f;
250 
251  fp = &coef[ord];
252  f = *fp;
253 
254  for ( fp--; fp >= coef; fp-- ) {
255  f = x * f + *fp;
256  }
257 
258  return(f);
259 }
260 
261 /*
262 * numchanges
263 *
264 * return the number of sign changes in the Sturm sequence in
265 * sseq at the value a.
266 */
267 
268 inline
269 int numchanges(int np, poly* sseq, double a)
270 {
271  int changes;
272  double f, lf;
273  poly *s;
274 
275  changes = 0;
276 
277  lf = evalpoly(sseq[0].ord, sseq[0].coef, a);
278 
279  for ( s = sseq + 1; s <= sseq + np; s++ ) {
280  f = evalpoly(s->ord, s->coef, a);
281  if ( lf == 0.0 || lf * f < 0 ) {
282  changes++;
283  }
284  lf = f;
285  }
286 
287  return(changes);
288 }
289 
290 /*
291 * modrf
292 *
293 * uses the modified regula-falsi method to evaluate the root
294 * in interval [a,b] of the polynomial described in coef. The
295 * root is returned is returned in *val. The routine returns zero
296 * if it can't converge.
297 */
298 
299 inline
300 int modrf(int ord, double *coef, double a, double b, double *val)
301 {
302  int its;
303  double fa, fb, x, fx, lfx;
304  double *fp, *scoef, *ecoef;
305  fx=0; // avoid uninitialized variable warning
306 
307  scoef = coef;
308  ecoef = &coef[ord];
309 
310  fb = fa = *ecoef;
311  for ( fp = ecoef - 1; fp >= scoef; fp-- ) {
312  fa = a * fa + *fp;
313  fb = b * fb + *fp;
314  }
315 
316  /*
317  * if there is no sign difference the method won't work
318  */
319  if ( fa * fb > 0.0 ) {
320  return(0);
321  }
322  /* commented out to avoid duplicate solutions when the bounds are close to the roots
323 
324  if (fabs(fa) < RELERROR)
325  {
326  *val = a;
327  return(1);
328  }
329 
330  if (fabs(fb) < RELERROR)
331  {
332  *val = b;
333  return(1);
334  }
335  */
336 
337  lfx = fa;
338 
339  for ( its = 0; its < MAX_ITER_SECANT; its++ ) {
340  x = (fb * a - fa * b) / (fb - fa);
341 
342  // constrain that x stays in the bounds
343  if ( x < a || x > b ) {
344  x = 0.5 * (a+b);
345  }
346  fx = *ecoef;
347  for ( fp = ecoef - 1; fp >= scoef; fp-- ) {
348  fx = x * fx + *fp;
349  }
350 
351  if ( fabs(x) > RELERROR ) {
352  if ( fabs(fx / x) < RELERROR ) {
353  *val = x;
354  return(1);
355  }
356  } else if ( fabs(fx) < RELERROR ) {
357  *val = x;
358  return(1);
359  }
360 
361  if ( (fa * fx) < 0 ) {
362  b = x;
363  fb = fx;
364  if ( (lfx * fx) > 0 ) {
365  fa /= 2;
366  }
367  } else {
368  a = x;
369  fa = fx;
370  if ( (lfx * fx) > 0 ) {
371  fb /= 2;
372  }
373  }
374  lfx = fx;
375  }
376 
377  //std::cout << "sturm.cc::modrf overflow " << a << " " << b << " " << fx << std::endl;
378  return(0);
379 }
380 
381 
382 /*
383 * sbisect
384 *
385 * uses a bisection based on the sturm sequence for the polynomial
386 * described in sseq to isolate intervals in which roots occur,
387 * the roots are returned in the roots array in order of magnitude.
388 *
389 * //DJM 6-20-07: added index to recurse through utility::vector1. Always
390 * //call sbisect with index=1. Will be increased during recursion.
391 */
392 //void sbisect(int np, poly* sseq, double min, double max, int atmin, int atmax, utility::vector1<double>& roots, int index)
393 
394 inline
395 void sbisect(int np, poly* sseq, double min, double max, int atmin, int atmax, double* roots)
396 {
397  double mid=0.0;
398  int n1 = 0, n2 = 0, its, atmid, nroot;
399 
400  if ( (nroot = atmin - atmax) == 1 ) {
401 
402  /*
403  * first try a less expensive technique.
404  */
405  //if (modrf(sseq->ord, sseq->coef, min, max, &roots[index])) {
406  if ( modrf(sseq->ord, sseq->coef, min, max, &roots[0]) ) {
407  return;
408  }
409 
410  /*
411  * if we get here we have to evaluate the root the hard
412  * way by using the Sturm sequence.
413  */
414  for ( its = 0; its < MAXIT; its++ ) {
415  mid = (min + max) / 2;
416  atmid = numchanges(np, sseq, mid);
417 
418  if ( fabs(mid) > RELERROR ) {
419  if ( fabs((max - min) / mid) < RELERROR ) {
420  //roots[index] = static_cast<Real> ( mid );
421  roots[0] = mid;
422  //std::cout << "sturm.cc::sbisect mid, min, max: " << mid << ", " << min << ", " << max << std::endl;
423  return;
424  }
425  } else if ( fabs(max - min) < RELERROR ) {
426  //roots[index] = static_cast<Real> ( mid );
427  roots[0] = mid;
428  //std::cout << "sturm.cc::sbisect mid, min, max: " << mid << ", " << min << ", " << max << std::endl;
429  return;
430  }
431 
432  if ( (atmin - atmid) == 0 ) {
433  min = mid;
434  } else {
435  max = mid;
436  }
437  }
438  if ( its == MAXIT ) {
439  //roots[index] = static_cast<Real> ( mid );
440  roots[0] = mid;
441  }
442  return;
443  }
444 
445  /*
446  * more than one root in the interval, we have to bisect...
447  */
448 
449  for ( its = 0; its < MAXIT; its++ ) {
450  mid = (min + max) / 2;
451  atmid = numchanges(np, sseq, mid);
452  n1 = atmin - atmid;
453  n2 = atmid - atmax;
454 
455  // DJM: this rarely occuring "artifact" causes crashing bugs so we take what we've got rather than proceed
456  if ( n1 < 0 ) {
457  //for (n1 = atmax; n1 < atmin; n1++) {
458  // roots[index+(n1 - atmax)] = static_cast<Real> ( mid );
459  //}
460  //std::cout << "atmin was less than atmid (" << atmin << " vs " << atmid << "). Exiting sturm prematurely." <<
461  //std::cout << "atmin was less than atmid (" << atmin << " vs " << atmid << "). But not exiting prematurely." <<
462  // std::endl;
463  //break;
464  }
465 
466  if ( n1 != 0 && n2 != 0 ) {
467  //sbisect(np, sseq, min, mid, atmin, atmid, roots, index);
468  //sbisect(np, sseq, mid, max, atmid, atmax, roots, index+n1);
469  sbisect(np, sseq, min, mid, atmin, atmid, roots);
470  sbisect(np, sseq, mid, max, atmid, atmax, &roots[n1]);
471  break;
472  }
473 
474  if ( n1 == 0 ) {
475  min = mid;
476  } else {
477  max = mid;
478  }
479  }
480 
481  if ( its == MAXIT ) {
482  //std::cout << "Maximum bisection iterations reached in sturm. Returning mid value for remaining solutions." << std::endl;
483  for ( n1 = atmax; n1 < atmin; n1++ ) {
484  //roots[index+(n1 - atmax)] = static_cast<Real> ( mid );
485  roots[n1 - atmax] = mid;
486  }
487  }
488 }
489 
490 inline
491 void solve_sturm(const int& p_order, int& n_root, const utility::vector1<double>& poly_coeffs, utility::vector1<double>& roots)
492 {
493  poly sseq[MAX_ORDER*2];
494  double min, max;
495  double roots_array[MAX_ORDER];
496  //double* roots_array;
497  //roots_array = new double[MAX_ORDER];
498  int order, i, nroots, nchanges, np, atmin, atmax;
499 
500  order = p_order;
501  // DJM: !!!here we are going from base1 vector to base0 array!!!
502  for ( i = order; i >= 0; i-- ) {
503  sseq[0].coef[i] = poly_coeffs[i+1];
504  }
505 
506  /*
507  * build the Sturm sequence
508  */
509  np = buildsturm(order, sseq);
510 
511  /* // DJM: Debug
512  if (basic::options::option[basic::options::OptionKeys::run::run_level] >= basic::options::verbose ) {
513  //std::cout << "sturm.cc::solve_sturm Sturm sequence for: " << std::endl;
514  for (int i = order; i >= 0; i--) {
515  //std::cout << sseq[0].coef[i] << " ";
516  }
517  //std::cout << std::endl << std::endl;
518  for (int i = 0; i <= np; i++) {
519  for (int j = sseq[i].ord; j >= 0; j--) {
520  //std::cout << sseq[i].coef[j] << " ";
521  }
522  //std::cout << std::endl;
523  }
524  }
525  */
526 
527  /*
528  * get the number of real roots
529  */
530 
531  nroots = numroots(np, sseq, &atmin, &atmax);
532 
533  if ( (nroots == 0) || !( (0 <= nroots) && (nroots <= MAX_ORDER)) ) { // make sure we have some sensible roots
534  n_root = 0;
535  return;
536  }
537 
538  roots.resize(nroots);
539 
540  //std::cout << "sturm.cc::solve_sturm: Number of real roots: " << nroots << std::endl;
541 
542  /*
543  * calculate the bracket that the roots live in
544  */
545  min = -1.0;
546  nchanges = numchanges(np, sseq, min);
547 
548  for ( i = 0; nchanges != atmin && i != MAXPOW; i++ ) {
549  min *= 10.0;
550  nchanges = numchanges(np, sseq, min);
551  }
552 
553  if ( nchanges != atmin ) {
554  //std::cout << "sturm.cc::solve: unable to bracket all negative roots" << std::endl;
555  atmin = nchanges;
556  }
557 
558  max = 1.0;
559  nchanges = numchanges(np, sseq, max);
560  for ( i = 0; nchanges != atmax && i != MAXPOW; i++ ) {
561  max *= 10.0;
562  nchanges = numchanges(np, sseq, max);
563  }
564 
565  if ( nchanges != atmax ) {
566  atmax = nchanges;
567  }
568 
569  nroots = atmin - atmax;
570 
571  //std::cout << "sturm.cc::solve_sturm atmin: " << atmin << std::endl;
572  //std::cout << "sturm.cc::solve_sturm atmax: " << atmax << std::endl;
573  //std::cout << "sturm.cc::solve_sturm nroots: " << nroots << std::endl;
574 
575  /*
576  * perform the bisection.
577  */
578 
579  //sbisect(np, sseq, min, max, atmin, atmax, roots, 1); // DJM: Vector1 is base 1
580  sbisect(np, sseq, min, max, atmin, atmax, roots_array);
581 
582  // DJM: copy the roots array into the roots vector
583  for ( numeric::Size i=1; i<= roots.size(); i++ ) {
584  roots[i]=roots_array[i-1];
585  }
586  //// DJM: now we can delete the roots array -- not if statically allocated
587  //delete [] roots_array;
588  //roots_array = NULL;
589 
590  n_root = nroots;
591 
592  /*
593  * write out the roots...
594  */
595  /* // DJM: debug
596  if (basic::options::option[basic::options::OptionKeys::run::run_level] >= basic::options::verbose ) {
597  if (nroots == 1) {
598  //std::cout << std::endl << "sturm.cc::solve_sturm 1 distinct real root at x = " << roots[1] << std::endl;
599  }
600  else {
601  //std::cout << nroots << " distinct real roots for x: " << std::endl;
602  for (i = 1; i <= nroots; i++) {
603  //std::cout << roots[i] << std::endl;
604  }
605  }
606  }
607  */
608  return;
609 }
610 
611 } // end namespace kinematic_closure
612 } // end namespace numeric
613 
614 #endif //INCLUDED_numeric_kinematic_closure_sturm_hh
void initialize_sturm(double *tol_secant, int *max_iter_sturm, int *max_iter_secant)
Definition: sturm.hh:54
double hyper_tan(double a, double x)
Definition: sturm.hh:62
vector1: std::vector with 1-based indexing: bool specialization
void sbisect(int np, poly *sseq, double min, double max, int atmin, int atmax, double *roots)
Definition: sturm.hh:395
platform::Size Size
Definition: types.hh:42
void solve_sturm(const int &p_order, int &n_root, const utility::vector1< double > &poly_coeffs, utility::vector1< double > &roots)
Definition: sturm.hh:491
def x
utility::keys::lookup::end< KeyType > const end
double evalpoly(int ord, double *coef, double x)
Definition: sturm.hh:247
#define MAXPOW
Definition: sturm.hh:34
short int min(short int const a, short int const b)
min( short int, short int )
short int max(short int const a, short int const b)
max( short int, short int )
double coef[MAX_ORDER+1]
Definition: sturm.hh:46
rosetta project type declarations. Should be kept updated with core/types.hh. This exists because num...
int modrf(int ord, double *coef, double a, double b, double *val)
Definition: sturm.hh:300
int buildsturm(int ord, poly *sseq)
Definition: sturm.hh:136
int modp(poly *u, poly *v, poly *r)
Definition: sturm.hh:88
struct numeric::kinematic_closure::p poly
int numchanges(int np, poly *sseq, double a)
Definition: sturm.hh:269
#define MAX_ORDER
Definition: sturm.hh:33
#define SMALL_ENOUGH
Definition: sturm.hh:35
int numroots(int np, poly *sseq, int *atneg, int *atpos)
Definition: sturm.hh:187