Numworks Epsilon  1.4.1
Graphing Calculator Operating System
cartesian_function.cpp
Go to the documentation of this file.
1 #include "cartesian_function.h"
2 #include <float.h>
3 #include <cmath>
4 
5 using namespace Poincare;
6 
7 namespace Graph {
8 
9 CartesianFunction::CartesianFunction(const char * text, KDColor color) :
10  Shared::Function(text, color),
11  m_displayDerivative(false)
12 {
13 }
14 
16  return m_displayDerivative;
17 }
18 
20  m_displayDerivative = display;
21 }
22 
25  Poincare::Expression * args[2] = {expression(context), &abscissa};
26  Poincare::Derivative derivative(args, true);
27  /* TODO: when we will simplify derivative, we might want to simplify the
28  * derivative here. However, we might want to do it once for all x (to avoid
29  * lagging in the derivative table. */
30  return derivative.approximateToScalar<double>(*context);
31 }
32 
33 double CartesianFunction::sumBetweenBounds(double start, double end, Poincare::Context * context) const {
36  Poincare::Expression * args[3] = {expression(context), &x, &y};
37  Poincare::Integral integral(args, true);
38  /* TODO: when we will simplify integral, we might want to simplify the
39  * integral here. However, we might want to do it once for all x (to avoid
40  * lagging in the derivative table. */
41  return integral.approximateToScalar<double>(*context);
42 }
43 
44 CartesianFunction::Point CartesianFunction::nextMinimumFrom(double start, double step, double max, Context * context) const {
45  return nextMinimumOfFunction(start, step, max, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1 = nullptr) {
46  return function0->evaluateAtAbscissa(x, context);
47  }, context);
48 }
49 
50 CartesianFunction::Point CartesianFunction::nextMaximumFrom(double start, double step, double max, Context * context) const {
51  Point minimumOfOpposite = nextMinimumOfFunction(start, step, max, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1 = nullptr) {
52  return -function0->evaluateAtAbscissa(x, context);
53  }, context);
54  return {.abscissa = minimumOfOpposite.abscissa, .value = -minimumOfOpposite.value};
55 }
56 
57 double CartesianFunction::nextRootFrom(double start, double step, double max, Context * context) const {
58  return nextIntersectionWithFunction(start, step, max, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1 = nullptr) {
59  return function0->evaluateAtAbscissa(x, context);
60  }, context, nullptr);
61 }
62 
63 CartesianFunction::Point CartesianFunction::nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, const Shared::Function * function) const {
64  double resultAbscissa = nextIntersectionWithFunction(start, step, max, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1) {
65  return function0->evaluateAtAbscissa(x, context)-function1->evaluateAtAbscissa(x, context);
66  }, context, function);
67  CartesianFunction::Point result = {.abscissa = resultAbscissa, .value = evaluateAtAbscissa(resultAbscissa, context)};
68  if (std::fabs(result.value) < step*k_precision) {
69  result.value = 0.0;
70  }
71  return result;
72 }
73 
74 CartesianFunction::Point CartesianFunction::nextMinimumOfFunction(double start, double step, double max, Evaluation evaluate, Context * context, const Shared::Function * function, bool lookForRootMinimum) const {
75  double bracket[3];
76  Point result = {.abscissa = NAN, .value = NAN};
77  double x = start;
78  bool endCondition = false;
79  do {
80  bracketMinimum(x, step, max, bracket, evaluate, context, function);
81  result = brentMinimum(bracket[0], bracket[2], evaluate, context, function);
82  x = bracket[1];
83  endCondition = std::isnan(result.abscissa) && (step > 0.0 ? x <= max : x >= max);
84  if (lookForRootMinimum) {
85  endCondition |= std::fabs(result.value) >= k_sqrtEps && (step > 0.0 ? x <= max : x >= max);
86  }
87  } while (endCondition);
88 
89  if (std::fabs(result.abscissa) < step*k_precision) {
90  result.abscissa = 0;
91  result.value = evaluate(0, context, this, function);
92  }
93  if (std::fabs(result.value) < step*k_precision) {
94  result.value = 0;
95  }
96  if (lookForRootMinimum) {
97  result.abscissa = std::fabs(result.value) >= k_sqrtEps ? NAN : result.abscissa;
98  }
99  return result;
100 }
101 
102 void CartesianFunction::bracketMinimum(double start, double step, double max, double result[3], Evaluation evaluate, Context * context, const Shared::Function * function) const {
103  Point p[3];
104  p[0] = {.abscissa = start, .value = evaluate(start, context, this, function)};
105  p[1] = {.abscissa = start+step, .value = evaluate(start+step, context, this, function)};
106  double x = start+2.0*step;
107  while (step > 0.0 ? x <= max : x >= max) {
108  p[2] = {.abscissa = x, .value = evaluate(x, context, this, function)};
109  if (p[0].value > p[1].value && p[2].value > p[1].value) {
110  result[0] = p[0].abscissa;
111  result[1] = p[1].abscissa;
112  result[2] = p[2].abscissa;
113  return;
114  }
115  if (p[0].value > p[1].value && p[1].value == p[2].value) {
116  } else {
117  p[0] = p[1];
118  p[1] = p[2];
119  }
120  x += step;
121  }
122  result[0] = NAN;
123  result[1] = NAN;
124  result[2] = NAN;
125 }
126 
128  return 'x';
129 }
130 
131 CartesianFunction::Point CartesianFunction::brentMinimum(double ax, double bx, Evaluation evaluate, Context * context, const Shared::Function * function) const {
132  /* Bibliography: R. P. Brent, Algorithms for finding zeros and extrema of
133  * functions without calculating derivatives */
134  if (ax > bx) {
135  return brentMinimum(bx, ax, evaluate, context, function);
136  }
137  double e = 0.0;
138  double a = ax;
139  double b = bx;
140  double x = a+k_goldenRatio*(b-a);
141  double v = x;
142  double w = x;
143  double fx = evaluate(x, context, this, function);
144  double fw = fx;
145  double fv = fw;
146 
147  double d = NAN;
148  double u, fu;
149 
150  for (int i = 0; i < 100; i++) {
151  double m = 0.5*(a+b);
152  double tol1 = k_sqrtEps*std::fabs(x)+1E-10;
153  double tol2 = 2.0*tol1;
154  if (std::fabs(x-m) <= tol2-0.5*(b-a)) {
155  double middleFax = evaluate((x+a)/2.0, context, this, function);
156  double middleFbx = evaluate((x+b)/2.0, context, this, function);
157  double fa = evaluate(a, context, this, function);
158  double fb = evaluate(b, context, this, function);
159  if (middleFax-fa <= k_sqrtEps && fx-middleFax <= k_sqrtEps && fx-middleFbx <= k_sqrtEps && middleFbx-fb <= k_sqrtEps) {
160  Point result = {.abscissa = x, .value = fx};
161  return result;
162  }
163  }
164  double p = 0;
165  double q = 0;
166  double r = 0;
167  if (std::fabs(e) > tol1) {
168  r = (x-w)*(fx-fv);
169  q = (x-v)*(fx-fw);
170  p = (x-v)*q -(x-w)*r;
171  q = 2.0*(q-r);
172  if (q>0.0) {
173  p = -p;
174  } else {
175  q = -q;
176  }
177  r = e;
178  e = d;
179  }
180  if (std::fabs(p) < std::fabs(0.5*q*r) && p<q*(a-x) && p<q*(b-x)) {
181  d = p/q;
182  u= x+d;
183  if (u-a < tol2 || b-u < tol2) {
184  d = x < m ? tol1 : -tol1;
185  }
186  } else {
187  e = x<m ? b-x : a-x;
188  d = k_goldenRatio*e;
189  }
190  u = x + (std::fabs(d) >= tol1 ? d : (d>0 ? tol1 : -tol1));
191  fu = evaluate(u, context, this, function);
192  if (fu <= fx) {
193  if (u<x) {
194  b = x;
195  } else {
196  a = x;
197  }
198  v = w;
199  fv = fw;
200  w = x;
201  fw = fx;
202  x = u;
203  fx = fu;
204  } else {
205  if (u<x) {
206  a = u;
207  } else {
208  b = u;
209  }
210  if (fu <= fw || w == x) {
211  v = w;
212  fv = fw;
213  w = u;
214  fw = fu;
215  } else if (fu <= fv || v == x || v == w) {
216  v = u;
217  fv = fu;
218  }
219  }
220  }
221  Point result = {.abscissa = NAN, .value = NAN};
222  return result;
223 }
224 
225 double CartesianFunction::nextIntersectionWithFunction(double start, double step, double max, Evaluation evaluation, Context * context, const Shared::Function * function) const {
226  double bracket[2];
227  double result = NAN;
228  static double precisionByGradUnit = 1E6;
229  double x = start+step;
230  do {
231  bracketRoot(x, step, max, bracket, evaluation, context, function);
232  result = brentRoot(bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, function);
233  x = bracket[1];
234  } while (std::isnan(result) && (step > 0.0 ? x <= max : x >= max));
235 
236  double extremumMax = std::isnan(result) ? max : result;
237  Point resultExtremum[2] = {
238  nextMinimumOfFunction(start, step, extremumMax, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1) {
239  if (function1) {
240  return function0->evaluateAtAbscissa(x, context)-function1->evaluateAtAbscissa(x, context);
241  } else {
242  return function0->evaluateAtAbscissa(x, context);
243  }
244  }, context, function, true),
245  nextMinimumOfFunction(start, step, extremumMax, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1) {
246  if (function1) {
247  return function1->evaluateAtAbscissa(x, context)-function0->evaluateAtAbscissa(x, context);
248  } else {
249  return -function0->evaluateAtAbscissa(x, context);
250  }
251  }, context, function, true)};
252  for (int i = 0; i < 2; i++) {
253  if (!std::isnan(resultExtremum[i].abscissa) && (std::isnan(result) || std::fabs(result - start) > std::fabs(resultExtremum[i].abscissa - start))) {
254  result = resultExtremum[i].abscissa;
255  }
256  }
257  if (std::fabs(result) < step*k_precision) {
258  result = 0;
259  }
260  return result;
261 }
262 
263 void CartesianFunction::bracketRoot(double start, double step, double max, double result[2], Evaluation evaluation, Context * context, const Shared::Function * function) const {
264  double a = start;
265  double b = start+step;
266  while (step > 0.0 ? b <= max : b >= max) {
267  double fa = evaluation(a, context, this, function);
268  double fb = evaluation(b, context, this, function);
269  if (fa*fb <= 0) {
270  result[0] = a;
271  result[1] = b;
272  return;
273  }
274  a = b;
275  b = b+step;
276  }
277  result[0] = NAN;
278  result[1] = NAN;
279 }
280 
281 
282 double CartesianFunction::brentRoot(double ax, double bx, double precision, Evaluation evaluation, Poincare::Context * context, const Shared::Function * function) const {
283  if (ax > bx) {
284  return brentRoot(bx, ax, precision, evaluation, context, function);
285  }
286  double a = ax;
287  double b = bx;
288  double c = bx;
289  double d = b-a;
290  double e = b-a;
291  double fa = evaluation(a, context, this, function);
292  double fb = evaluation(b, context, this, function);
293  double fc = fb;
294  for (int i = 0; i < 100; i++) {
295  if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
296  c = a;
297  fc = fa;
298  e = b-a;
299  d = b-a;
300  }
301  if (std::fabs(fc) < std::fabs(fb)) {
302  a = b;
303  b = c;
304  c = a;
305  fa = fb;
306  fb = fc;
307  fc = fa;
308  }
309  double tol1 = 2.0*DBL_EPSILON*std::fabs(b)+0.5*precision;
310  double xm = 0.5*(c-b);
311  if (std::fabs(xm) <= tol1 || fb == 0.0) {
312  double fbcMiddle = evaluation(0.5*(b+c), context, this, function);
313  double isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb);
314  if (isContinuous) {
315  return b;
316  }
317  }
318  if (std::fabs(e) >= tol1 && std::fabs(fa) > std::fabs(b)) {
319  double s = fb/fa;
320  double p = 2.0*xm*s;
321  double q = 1.0-s;
322  if (a != c) {
323  q = fa/fc;
324  double r = fb/fc;
325  p = s*(2.0*xm*q*(q-r)-(b-a)*(r-1.0));
326  q = (q-1.0)*(r-1.0)*(s-1.0);
327  }
328  q = p > 0.0 ? -q : q;
329  p = std::fabs(p);
330  double min1 = 3.0*xm*q-std::fabs(tol1*q);
331  double min2 = std::fabs(e*q);
332  if (2.0*p < (min1 < min2 ? min1 : min2)) {
333  e = d;
334  d = p/q;
335  } else {
336  d = xm;
337  e =d;
338  }
339  } else {
340  d = xm;
341  e = d;
342  }
343  a = b;
344  fa = fb;
345  if (std::fabs(d) > tol1) {
346  b += d;
347  } else {
348  b += xm > 0.0 ? tol1 : tol1;
349  }
350  fb = evaluation(b, context, this, function);
351  }
352  return NAN;
353 }
354 
355 }
Point nextMaximumFrom(double start, double step, double max, Poincare::Context *context) const
double sumBetweenBounds(double start, double end, Poincare::Context *context) const override
#define NAN
Definition: math.h:30
void(* Function)(const char *input)
Definition: command.h:11
void setDisplayDerivative(bool display)
#define fabs(x)
Definition: math.h:178
Definition: app.cpp:9
#define DBL_EPSILON
Definition: float.h:11
Poincare::Expression * expression(Poincare::Context *context) const
Definition: function.cpp:72
c(generic_all_nodes)
double approximateDerivative(double x, Poincare::Context *context) const
double nextRootFrom(double start, double step, double max, Poincare::Context *context) const
char symbol() const override
args
Definition: i18n.py:175
#define false
Definition: stdbool.h:9
#define isnan(x)
Definition: math.h:43
static Complex< T > Float(T x)
Definition: complex.cpp:23
void start()
Definition: rt0.cpp:31
Definition: color.h:6
virtual float evaluateAtAbscissa(float x, Poincare::Context *context) const
Definition: function.h:30
T approximateToScalar(Context &context, AngleUnit angleUnit=AngleUnit::Default) const
Definition: expression.cpp:347
Point nextIntersectionFrom(double start, double step, double max, Poincare::Context *context, const Shared::Function *function) const
Point nextMinimumFrom(double start, double step, double max, Poincare::Context *context) const