Ocean
Equation.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_MATH_EQUATION_H
9 #define META_OCEAN_MATH_EQUATION_H
10 
11 #include "ocean/math/Math.h"
12 #include "ocean/math/Numeric.h"
13 
14 #include "ocean/base/Utilities.h"
15 
16 #include <complex>
17 
18 namespace Ocean
19 {
20 
21 // Forward declaration.
22 template <typename T> class EquationT;
23 
24 /**
25  * Definition of the Equation object, depending on the OCEAN_MATH_USE_SINGLE_PRECISION either with single or double precision float data type.
26  * @see EquationT
27  * @ingroup math
28  */
30 
31 /**
32  * Definition of the Equation class using double values
33  * @see EquationT
34  * @ingroup math
35  */
37 
38 /**
39  * Definition of the Equation class using float values
40  * @see EquationT
41  * @ingroup math
42  */
44 
45 /**
46  * This class provides several functions to solve equations with different degree using floating point values with the precission specified by type T.
47  * @tparam T Type of passed floating point values
48  * @see Equation, EquationF, EquationD.
49  * @ingroup math
50  */
51 template <typename T>
52 class EquationT
53 {
54  public:
55 
56  /**
57  * Solves a linear eqation with the form:<br>
58  * ax + b = 0
59  * @param a A parameter, with range (-infinity, infinity) \ {0}, (must not be 0)
60  * @param b B parameter, with range (-infinity, infinity)
61  * @param x Resulting solution
62  * @return True, if succeeded
63  */
64  static bool solveLinear(const T a, const T b, T& x);
65 
66  /**
67  * Solves an quadratic equation with the form:<br>
68  * ax^2 + bx + c = 0
69  * @param a A parameter, with range (-infinity, infinity) \ {0}, (must not be 0)
70  * @param b B parameter, with range (-infinity, infinity)
71  * @param c C parameter, with range (-infinity, infinity)
72  * @param x1 First resulting solution, with range (-infinity, infinity)
73  * @param x2 Second resulting solution, with range (-infinity, infinity)
74  * @return True, if succeeded
75  */
76  static bool solveQuadratic(const T a, const T b, const T c, T& x1, T& x2);
77 
78  /**
79  * Solves a cubic equation with the from:<br>
80  * ax^3 + bx^2 + cx + d = 0
81  * @param a A parameter, with range (-infinity, infinity) \ {0}, (must not be 0)
82  * @param b B parameter, with range (-infinity, infinity)
83  * @param c C parameter, with range (-infinity, infinity)
84  * @param d D parameter, with range (-infinity, infinity)
85  * @param x1 First resulting solution, with range (-infinity, infinity)
86  * @param x2 Second resulting solution, with range (-infinity, infinity)
87  * @param x3 Third resulting solution, with range (-infinity, infinity)
88  * @return Number of solutions
89  */
90  static unsigned int solveCubic(const T a, const T b, const T c, const T d, T& x1, T& x2, T& x3);
91 
92  /**
93  * Solves a quartic equation with the form:<br>
94  * ax^4 + bx^3 + cx^2 + dx + e = 0
95  * @param a A parameter, with range (-infinity, infinity) \ {0}, (must not be 0)
96  * @param b B parameter, with range (-infinity, infinity)
97  * @param c C parameter, with range (-infinity, infinity)
98  * @param d D parameter, with range (-infinity, infinity)
99  * @param e E parameter, with range (-infinity, infinity)
100  * @param x Array with at least four scalar values receiving the (at most) four solutions
101  * @return Number of solutions
102  */
103  static unsigned int solveQuartic(const T a, const T b, const T c, const T d, const T e, T* x);
104 };
105 
106 template <typename T>
107 bool EquationT<T>::solveLinear(const T a, const T b, T& x)
108 {
109  ocean_assert(NumericT<T>::isNotEqualEps(a));
110 
111  // ax + b = 0
112 
114  {
115  return false;
116  }
117 
118  x = -b / a;
119  return true;
120 }
121 
122 template <typename T>
123 bool EquationT<T>::solveQuadratic(const T a, const T b, const T c, T& x1, T& x2)
124 {
125  ocean_assert(NumericT<T>::isNotEqualEps(a));
126 
127  // ax^2 + bx + c = 0
128  // see Numerical Recipes in C++
129 
131  {
132  return false;
133  }
134 
135  const T value = b * b - 4 * a * c;
136  if (!NumericT<T>::isAbove(value, 0))
137  {
138  return false;
139  }
140 
141  const T q = T(-0.5) * (b + ((value > T(0)) ? NumericT<T>::copySign(NumericT<T>::sqrt(value), b) : T(0)));
142 
144  {
145  x1 = 0;
146  x2 = 0;
147  return true;
148  }
149 
150  x1 = q / a;
151  x2 = c / q;
152 
153 #ifdef OCEAN_DEBUG
154  if (std::is_same<T, float>::value)
155  {
156  // for 32 bit float values we have to weaken the zero accuracy
157  ocean_assert(NumericT<T>::isEqual(a * x1 * x1 + b * x1 + c, T(0), NumericT<T>::weakEps() * NumericT<T>::abs(x1)));
158  ocean_assert(NumericT<T>::isEqual(a * x2 * x2 + b * x2 + c, T(0), NumericT<T>::weakEps() * NumericT<T>::abs(x2)));
159  }
160  else
161  {
162  ocean_assert(NumericT<T>::isWeakEqualEps(a * x1 * x1 + b * x1 + c));
163  ocean_assert(NumericT<T>::isWeakEqualEps(a * x2 * x2 + b * x2 + c));
164  }
165 #endif
166 
167  return true;
168 }
169 
170 template <typename T>
171 unsigned int EquationT<T>::solveCubic(const T a, const T b, const T c, const T d, T& x1, T& x2, T& x3)
172 {
173  ocean_assert(NumericT<T>::isNotEqualEps(a));
174 
175  // ax^3 + bx^2 + cx + d = 0
176  // see Numerical Recipes in C++
177 
179  {
180  return 0u;
181  }
182 
183  const T a1 = 1 / a;
184  const T alpha = b * a1;
185  const T beta = c * a1;
186  const T gamma = d * a1;
187 
188  // x^3 + alpha x^2 + beta x + gamma = 0
189 
190  // alpha2 = alpha^2
191  const T alpha2 = alpha * alpha;
192 
193  // q = (alpha^2 - 3b) / 9
194  const T q = (alpha2 - 3 * beta) * T(0.11111111111111111111111111111111);
195 
196  // r = (2 alpha^3 - 9 alpha beta + 27gamma) / 54
197  const T r = (2 * alpha2 * alpha - 9 * alpha * beta + 27 * gamma) * T(0.018518518518518518518518518518519);
198 
199  // r2 = r^2
200  const T r2 = r * r;
201 
202  // q3 = q^3
203  const T q3 = q * q * q;
204 
205  if (r2 <= q3 + Numeric::eps() && q > NumericT<T>::eps())
206  {
207  const T sqrtQ = NumericT<T>::sqrt(q);
208 
209  // angle = arccos(r / sqrt(q^3))
210  // angle_3 = angle / 3
211  const T angle_3 = NumericT<T>::acos(minmax<T>(-1, r / (q * sqrtQ), 1)) * T(0.33333333333333333333333333333333);
212 
213  // alpha_3 = alpha / 3
214  const T alpha_3 = alpha * T(0.33333333333333333333333333333333);
215 
216  const T factor = -2 * sqrtQ;
217 
218  // x1 = -2 sqrt(q) * cos(angle / 3) - alpha / 3
219  x1 = factor * NumericT<T>::cos(angle_3) - alpha_3;
220 
221  // x2 = -2 sqrt(q) * cos((angle + 2pi) / 3) - alpha / 3
222  x2 = factor * NumericT<T>::cos(angle_3 + T(2.0943951023931954923084289221863)) - alpha_3;
223 
224  // x3 = -2 sqrt(q) * cos((angle - 2pi) / 3) - alpha / 3
225  x3 = factor * NumericT<T>::cos(angle_3 - T(2.0943951023931954923084289221863)) - alpha_3;
226 
227 #ifdef OCEAN_DEBUG
228 
229  const T value1 = a * x1 * x1 * x1 + b * x1 * x1 + c * x1 + d;
230  const T value2 = a * x2 * x2 * x2 + b * x2 * x2 + c * x2 + d;
231  const T value3 = a * x3 * x3 * x3 + b * x3 * x3 + c * x3 + d;
232 
233  // the accuracy for 32 bit float values may be very poor so that we cannot define any assert
234  if (!std::is_same<T, float>::value)
235  {
236  ocean_assert(NumericT<T>::isEqual(value1, T(0), T(1e-3)));
237  ocean_assert(NumericT<T>::isEqual(value2, T(0), T(1e-3)));
238  ocean_assert(NumericT<T>::isEqual(value3, T(0), T(1e-3)));
239  }
240 #endif
241 
242  return 3u;
243  }
244 
245  ocean_assert(r2 - q3 >= -Numeric::eps());
246 
247  // m = -sign(r) * [abs(r) + sqrt(r^2 - q^3)]^(1/3)
248  const T m = -NumericT<T>::copySign(pow(NumericT<T>::abs(r) + NumericT<T>::sqrt(std::max(T(0), r2 - q3)), T(0.33333333333333333333333333333333)), r);
249 
250  // n = 0, if m == 0
251  // n = q / m, if m != 0
252  const T n = NumericT<T>::isEqualEps(m) ? 0 : q / m;
253 
254  // x1 = (m + n) - alpha / 3
255  x1 = m + n - alpha * T(0.33333333333333333333333333333333);
256 
257 #ifdef OCEAN_DEBUG
258 
259  const T value1 = a * x1 * x1 * x1 + b * x1 * x1 + c * x1 + d;
260 
261  // the accuracy for 32 bit float values may be very poor so that we cannot define any assert
262  if (!std::is_same<T, float>::value)
263  {
264  ocean_assert(NumericT<T>::isEqual(value1, T(0), T(1e-3)));
265  }
266 
267 #endif
268 
269  return 1u;
270 }
271 
272 template <typename T>
273 unsigned int EquationT<T>::solveQuartic(const T a, const T b, const T c, const T d, const T e, T* x)
274 {
275  ocean_assert(NumericT<T>::isNotEqualEps(a));
276  ocean_assert(x != nullptr);
277 
278  // ax^4 + bx^3 + cx^2 + dx + e = 0
279 
281  {
282  return 0u;
283  }
284 
285  // simplification using substitution:
286  /// y^4 + alpha * y^2 + beta * y + gamma = 0
287 
288  // 1 / a
289  const T a1 = T(1.0) / a;
290 
291  // b / a
292  const T b_a = b * a1;
293 
294  // c / a
295  const T c_a = c * a1;
296 
297  // (b * b) / (a * a)
298  const T b_a2 = b_a * b_a;
299 
300  // (b * b * b) / (a * a * a)
301  const T b_a3 = b_a2 * b_a;
302 
303  // d / a
304  const T d_a = d * a1;
305 
306  //const T alpha = T(-0.375) * (b / a) * (b / a) + c / a;
307  const T alpha = T(-0.375) * b_a2 + c_a;
308 
309  //const T beta = T(0.125) * (b / a) * (b / a) * (b / a) - T(0.5) * (b / a) * (c / a) + (d / a);
310  const T beta = T(0.125) * b_a3 - T(0.5) * b_a * c_a + d_a;
311 
312  //const T gamma = T(-0.01171875) * (b / a) * (b / a) * (b / a) * (b / a) + T(0.0625) * (b / a) * (b / a) * (c / a) - T(0.25) * (b / a) * (d / a) + e / a;
313  const T gamma = T(-0.01171875) * b_a3 * b_a + T(0.0625) * b_a2 * c_a - T(0.25) * b_a * d_a + e * a1;
314 
315  // y^4 + alpha y^2 + beta y + gamma = 0
316 
317  if (NumericT<T>::isEqualEps(beta))
318  {
319  const std::complex<T> cx1 = std::complex<T>(T(-0.25)) * std::complex<T>(b / a) + NumericT<T>::sqrt(std::complex<T>(T(0.5)) * (std::complex<T>(-alpha) + NumericT<T>::sqrt(std::complex<T>(alpha * alpha - 4 * gamma))));
320  const std::complex<T> cx2 = std::complex<T>(T(-0.25)) * std::complex<T>(b / a) + NumericT<T>::sqrt(std::complex<T>(T(0.5)) * (std::complex<T>(-alpha) - NumericT<T>::sqrt(std::complex<T>(alpha * alpha - 4 * gamma))));
321  const std::complex<T> cx3 = std::complex<T>(T(-0.25)) * std::complex<T>(b / a) - NumericT<T>::sqrt(std::complex<T>(T(0.5)) * (std::complex<T>(-alpha) + NumericT<T>::sqrt(std::complex<T>(alpha * alpha - 4 * gamma))));
322  const std::complex<T> cx4 = std::complex<T>(T(-0.25)) * std::complex<T>(b / a) - NumericT<T>::sqrt(std::complex<T>(T(0.5)) * (std::complex<T>(-alpha) - NumericT<T>::sqrt(std::complex<T>(alpha * alpha - 4 * gamma))));
323 
324  ocean_assert((std::is_same<T, float>::value) || NumericT<T>::isWeakEqualEps(cx1 * cx1 * cx1 * cx1 * a + cx1 * cx1 * cx1 * b + cx1 * cx1 * c + cx1 * d + e));
325  ocean_assert((std::is_same<T, float>::value) || NumericT<T>::isWeakEqualEps(cx2 * cx2 * cx2 * cx2 * a + cx2 * cx2 * cx2 * b + cx2 * cx2 * c + cx2 * d + e));
326  ocean_assert((std::is_same<T, float>::value) || NumericT<T>::isWeakEqualEps(cx3 * cx3 * cx3 * cx3 * a + cx3 * cx3 * cx3 * b + cx3 * cx3 * c + cx3 * d + e));
327  ocean_assert((std::is_same<T, float>::value) || NumericT<T>::isWeakEqualEps(cx4 * cx4 * cx4 * cx4 * a + cx4 * cx4 * cx4 * b + cx4 * cx4 * c + cx4 * d + e));
328 
329  unsigned int solutions = 0u;
330 
331  if (NumericT<T>::isEqualEps(cx1.imag()))
332  {
333  const T solution = cx1.real();
334  if (NumericT<T>::isWeakEqualEps(solution * solution * solution * solution * a + solution * solution * solution * b + solution * solution * c + solution * d + e))
335  {
336  x[solutions++] = solution;
337  }
338  }
339 
340  if (NumericT<T>::isEqualEps(cx2.imag()))
341  {
342  const T solution = cx2.real();
343  if (NumericT<T>::isWeakEqualEps(solution * solution * solution * solution * a + solution * solution * solution * b + solution * solution * c + solution * d + e))
344  {
345  x[solutions++] = solution;
346  }
347  }
348 
349  if (NumericT<T>::isEqualEps(cx3.imag()))
350  {
351  const T solution = cx3.real();
352  if (NumericT<T>::isWeakEqualEps(solution * solution * solution * solution * a + solution * solution * solution * b + solution * solution * c + solution * d + e))
353  {
354  x[solutions++] = solution;
355  }
356  }
357 
358  if (NumericT<T>::isEqualEps(cx4.imag()))
359  {
360  const T solution = cx4.real();
361  if (NumericT<T>::isWeakEqualEps(solution * solution * solution * solution * a + solution * solution * solution * b + solution * solution * c + solution * d + e))
362  {
363  x[solutions++] = solution;
364  }
365  }
366 
367  return solutions;
368  }
369 
370  //const std::complex<T> p(-(alpha * alpha) / T(12.0) - gamma);
371  const std::complex<T> p(T(-0.08333333333333333333333333333333) * alpha * alpha - gamma);
372 
373  //const std::complex<T> q(-(alpha * alpha * alpha) / T(108.0) + (alpha * gamma) / T(3.0) - (beta * beta) / T(8.0));
374  const std::complex<T> q(T(-0.00925925925925925925925925925926) * alpha * alpha * alpha + T(0.33333333333333333333333333333333) * alpha * gamma - T(0.125) * beta * beta);
375 
376  const std::complex<T> qqSqr = NumericT<T>::sqrt(std::complex<T>(T(0.25)) * q * q + std::complex<T>(T(0.03703703703703703703703703703704)) * p * p * p);
377 
378  const std::complex<T> r(std::complex<T>(T(-0.5)) * q + qqSqr);
380  {
381  return 0u;
382  }
383 
384  const std::complex<T> u = NumericT<T>::pow(std::complex<T>(r), T(0.33333333333333333333333333333333));
385 
387  {
388  return 0u;
389  }
390 
391  std::complex<T> y;
392  if (NumericT<T>::isEqualEps(u.real()) && NumericT<T>::isEqualEps(u.imag()))
393  {
394  y = std::complex<T>(T(-0.83333333333333333333333333333333) * alpha) + u - NumericT<T>::pow(std::complex<T>(q), T(0.33333333333333333333333333333333));
395  }
396  else
397  {
398  y = std::complex<T>(T(-0.83333333333333333333333333333333) * alpha) + u - p / (std::complex<T>(3) * u);
399  }
400 
401  //const std::complex<T> w_(NumericT<T>::sqrt(std::complex<T>(alpha) + std::complex<T>(2) * y));
402  const std::complex<T> w(NumericT<T>::sqrt(std::complex<T>(T(0.25) * alpha) + std::complex<T>(T(0.5)) * y));
403 
404  //const std::complex<T> cx1 = std::complex<T>(T(-0.25)) * std::complex<T>(b / a) + std::complex<T>(T(0.5)) * (w + NumericT<T>::sqrt(std::complex<T>(-1) * (std::complex<T>(3) * std::complex<T>(alpha) + std::complex<T>(2) * y + std::complex<T>(2) * std::complex<T>(beta) / w)));
405 
407  {
408  return 0u;
409  }
410 
411  const std::complex<T> beta2_w(std::complex<T>(T(-0.25) * beta) / w);
412  const std::complex<T> alpha3y2 = std::complex<T>(T(-0.75) * alpha) - std::complex<T>(T(0.5)) * y;
413  const std::complex<T> b_a4(T(-0.25) * b_a);
414 
415  const std::complex<T> sqrtPositive(NumericT<T>::sqrt(alpha3y2 + beta2_w));
416  const std::complex<T> sqrtNegative(NumericT<T>::sqrt(alpha3y2 - beta2_w));
417 
418  const std::complex<T> cx1 = b_a4 + w + sqrtPositive;
419  const std::complex<T> cx2 = b_a4 + w - sqrtPositive;
420  const std::complex<T> cx3 = b_a4 - w + sqrtNegative;
421  const std::complex<T> cx4 = b_a4 - w - sqrtNegative;
422 
423  unsigned int solutions = 0u;
424 
425  if (NumericT<T>::isEqualEps(cx1.imag()))
426  {
427  const T solution = cx1.real();
428  if (NumericT<T>::isWeakEqualEps(solution * solution * solution * solution * a + solution * solution * solution * b + solution * solution * c + solution * d + e))
429  {
430  x[solutions++] = solution;
431  }
432  }
433 
434  if (NumericT<T>::isEqualEps(cx2.imag()))
435  {
436  const T solution = cx2.real();
437  if (NumericT<T>::isWeakEqualEps(solution * solution * solution * solution * a + solution * solution * solution * b + solution * solution * c + solution * d + e))
438  {
439  x[solutions++] = solution;
440  }
441  }
442 
443  if (NumericT<T>::isEqualEps(cx3.imag()))
444  {
445  const T solution = cx3.real();
446  if (NumericT<T>::isWeakEqualEps(solution * solution * solution * solution * a + solution * solution * solution * b + solution * solution * c + solution * d + e))
447  {
448  x[solutions++] = solution;
449  }
450  }
451 
452  if (NumericT<T>::isEqualEps(cx4.imag()))
453  {
454  const T solution = cx4.real();
455  if (NumericT<T>::isWeakEqualEps(solution * solution * solution * solution * a + solution * solution * solution * b + solution * solution * c + solution * d + e))
456  {
457  x[solutions++] = solution;
458  }
459  }
460 
461  return solutions;
462 }
463 
464 }
465 
466 #endif // META_OCEAN_MATH_EQUATION_H
This class provides several functions to solve equations with different degree using floating point v...
Definition: Equation.h:53
static unsigned int solveCubic(const T a, const T b, const T c, const T d, T &x1, T &x2, T &x3)
Solves a cubic equation with the from: ax^3 + bx^2 + cx + d = 0.
Definition: Equation.h:171
static bool solveQuadratic(const T a, const T b, const T c, T &x1, T &x2)
Solves an quadratic equation with the form: ax^2 + bx + c = 0.
Definition: Equation.h:123
static bool solveLinear(const T a, const T b, T &x)
Solves a linear eqation with the form: ax + b = 0.
Definition: Equation.h:107
static unsigned int solveQuartic(const T a, const T b, const T c, const T d, const T e, T *x)
Solves a quartic equation with the form: ax^4 + bx^3 + cx^2 + dx + e = 0.
Definition: Equation.h:273
This class provides basic numeric functionalities.
Definition: Numeric.h:57
static T pow(const T x, const T y)
Returns x raised to the power of y.
Definition: Numeric.h:1860
static T sqrt(const T value)
Returns the square root of a given value.
Definition: Numeric.h:1533
static constexpr T eps()
Returns a small epsilon.
static constexpr bool isEqualEps(const T value)
Returns whether a value is smaller than or equal to a small epsilon.
Definition: Numeric.h:2087
static constexpr T copySign(const T signReceiver, const T signProvider)
Copies the sign of a given value to another one.
Definition: Numeric.h:3264
static T cos(const T value)
Returns the cosine of a given value.
Definition: Numeric.h:1584
static T acos(const T value)
Returns the arccosine of a given value.
Definition: Numeric.h:2907
EquationT< Scalar > Equation
Definition of the Equation object, depending on the OCEAN_MATH_USE_SINGLE_PRECISION either with singl...
Definition: Equation.h:22
EquationT< float > EquationF
Definition of the Equation class using float values.
Definition: Equation.h:43
EquationT< double > EquationD
Definition of the Equation class using double values.
Definition: Equation.h:36
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15