Tomographer  v1.0a
Tomographer C++ Framework Documentation
simple_find_zero.h
Go to the documentation of this file.
1 /* This file is part of the Tomographer project, which is distributed under the
2  * terms of the MIT license.
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2015 ETH Zurich, Institute for Theoretical Physics, Philippe Faist
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
27 #ifndef SIMPLE_FIND_ZERO_H
28 #define SIMPLE_FIND_ZERO_H
29 
41 #include <cmath>
42 #include <cstdio>
43 #include <limits>
44 
46 
47 namespace Tomographer
48 {
49 namespace Tools
50 {
51 
52 
66 template<typename ValueType, typename PointType, typename Fn, typename LoggerType = Logger::VacuumLogger>
67 inline static PointType simpleFindZero(const Fn & fn, PointType pt1, PointType pt2,
68  int maxiters = 50,
69  ValueType valtolerance = 1e-6,
70  ValueType * final_value = NULL,
71  int * final_numiters = NULL,
72  LoggerType & logger = Logger::vacuum_logger)
73 {
74  int num_recover_iters = 0;
75  int max_recover_iters = 10*maxiters;
76 
77  // initialize to invalid values
78  if (final_value != NULL) {
80  }
81  if (final_numiters != NULL) {
82  *final_numiters = -1;
83  }
84 
85  // start now
86  ValueType val1 = fn(pt1);
87  ValueType val2 = fn(pt2);
88  while (std::isnan(val2) && num_recover_iters < max_recover_iters) {
89  logger.longdebug("simpleFindZero()", [pt2](std::ostream & str) {
90  str << "function returned NaN for given pt2 = " << pt2 << ", attempting to recover...";
91  });
92  // make pt2 closer to pt1
93  pt2 = pt1 + (pt2 - pt1) / 2.0;
94  val2 = fn(pt2);
95  ++num_recover_iters;
96  }
97 
98  if (std::fabs(val2 - val1) <= valtolerance) {
99  logger.warning("simpleFindZero()", [pt1,pt2,val1,val2](std::ostream & str) {
100  str << "the two initial points x1="<<pt1<<" and x2="<<pt2<<" give values too close: y1="<<val1
101  << " and y2=" << val2;
102  });
104  }
105 
106  PointType pt3 = pt1 - val1 * (pt2 - pt1)/(val2 - val1);
107  ValueType val3 = fn(pt3);
108  if (std::isnan(val3) || (std::fabs(val3 - val2) <= valtolerance ||
109  std::fabs(val3 - val1) <= valtolerance)) {
110  logger.longdebug("simpleFindZero()", [pt3,val3](std::ostream & str) {
111  str << "function returned NaN, or value too close to x1 or x2, for guessed x3=" << pt3
112  << " (y3="<<val3<<"), attempting to recover...";
113  });
114  // pick point between pt1 and pt2
115  pt3 = pt1 + (pt2 - pt1) / 2.0;
116  val3 = fn(pt3);
117  }
118 
119  logger.longdebug("simpleFindZero()", [&](std::ostream& str) {
120  str << "Starting with\n"
121  << "\t x1="<<pt1<<" \tf(x1)="<<val1<<"\n"
122  << "\t x2="<<pt2<<" \tf(x2)="<<val2<<"\n"
123  << "\t --> x3="<<pt3<<"\tf(x3)="<<val3;
124  });
125 
126  PointType newpt;
127  ValueType newval;
128 
129  for (int k = 0; k < maxiters; ++k) {
130  // iterate x_{n+1} = ...
131  newpt = pt1 * val2*val3 / ((val1 - val2)*(val1 - val3))
132  + pt2 * val1*val3 / ((val2 - val1)*(val2 - val3))
133  + pt3 * val1*val2 / ((val3 - val1)*(val3 - val2)) ;
134 
135  // and calculate f(x_{n+1})
136  newval = fn(newpt);
137 
138  while (std::isnan(newval) && num_recover_iters < max_recover_iters) {
139  // try to recover.
140  logger.longdebug("simpleFindZero()", [newpt](std::ostream & str) {
141  str << "function returned NaN for new point " << newpt << ", attempting to recover...";
142  });
143  newpt = pt3 + (newpt - pt3) / 2;
144  newval = fn(newpt);
145  ++num_recover_iters;
146  }
147 
148  logger.longdebug("simpleFindZero()", [&](std::ostream& str) {
149  str << "Iter #" << k << ": x = " << newpt << " y = " << newval;
150  });
151 
152  // if the result is precise enough, return it.
153  // same if we need to stop because we've exceeded maximum recovery attempts
154  if (std::fabs(newval) < valtolerance || num_recover_iters >= max_recover_iters) {
155  if (final_value != NULL) {
156  *final_value = newval;
157  }
158  if (final_numiters != NULL) {
159  *final_numiters = k;
160  }
161  return newpt;
162  }
163  // recurrence
164  pt1 = pt2; val1 = val2;
165  pt2 = pt3; val2 = val3;
166  pt3 = newpt; val3 = newval;
167  }
168 
169  if (final_value != NULL) {
170  *final_value = newval;
171  }
172  if (final_numiters != NULL) {
173  *final_numiters = maxiters;
174  }
175  return newpt;
176 }
177 
178 
179 
180 } // namespace Tools
181 } // namespace Tomographer
182 
183 
184 
185 #endif
Base namespace for the Tomographer project.
Definition: dmmhrw.h:51
T fabs(T...args)
T isnan(T...args)
T quiet_NaN(T...args)
STL class.
Utilities for logging messages.