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