Commit d1a9c638 by Timo Koch

### [math][invertCubicPoly] Improve doc. Add possibility to specify...

`[math][invertCubicPoly] Improve doc. Add possibility to specify post-processing Newton iterations to increase precision`
parent e070e79b
 ... @@ -198,25 +198,30 @@ int invertQuadraticPolynomial(SolContainer &sol, ... @@ -198,25 +198,30 @@ int invertQuadraticPolynomial(SolContainer &sol, template template void invertCubicPolynomialPostProcess_(SolContainer &sol, void invertCubicPolynomialPostProcess_(SolContainer &sol, int numSol, int numSol, Scalar a, Scalar a, Scalar b, Scalar c, Scalar d, Scalar b, std::size_t numIterations = 1) Scalar c, Scalar d) { { // do one Newton iteration on the analytic solution if the const auto eval = [&](auto x){ return d + x*(c + x*(b + x*a)); }; // precision is increased const auto evalDeriv = [&](auto x){ return c + x*(2*b + x*3*a); }; for (int i = 0; i < numSol; ++i) { Scalar x = sol[i]; Scalar fOld = d + x*(c + x*(b + x*a)); Scalar fPrime = c + x*(2*b + x*3*a); // do numIterations Newton iterations on the analytic solution if (fPrime == 0.0) // and update result if the precision is increased continue; for (int i = 0; i < numSol; ++i) x -= fOld/fPrime; { Scalar x = sol[i]; Scalar fCurrent = eval(x); const Scalar fOld = fCurrent; for (int j = 0; j < numIterations; ++j) { const Scalar fPrime = evalDeriv(x); if (fPrime == 0.0) break; x -= fCurrent/fPrime; fCurrent = eval(x); } Scalar fNew = d + x*(c + x*(b + x*a)); using std::abs; using std::abs; if (abs(fNew) < abs(fOld)) if (abs(fCurrent) < abs(fOld)) sol[i] = x; sol[i] = x; } } } } ... @@ -229,22 +234,24 @@ void invertCubicPolynomialPostProcess_(SolContainer &sol, ... @@ -229,22 +234,24 @@ void invertCubicPolynomialPostProcess_(SolContainer &sol, * The polynomial is defined as * The polynomial is defined as * \f[ p(x) = a\; x^3 + + b\;x^3 + c\;x + d \f] * \f[ p(x) = a\; x^3 + + b\;x^3 + c\;x + d \f] * * * This method teturns the number of solutions which are in the real * This method returns the number of solutions which are in the real * numbers. The "sol" argument contains the real roots of the cubic * numbers. The "sol" argument contains the real roots of the cubic * polynomial in order with the smallest root first. * polynomial in order with the smallest root first. * * * \note The closer the roots are to each other the less * precise the inversion becomes. Increase number of post-processing iterations for improved results. * * \param sol Container into which the solutions are written * \param sol Container into which the solutions are written * \param a The coefficiont for the cubic term * \param a The coefficient for the cubic term * \param b The coefficiont for the quadratic term * \param b The coefficient for the quadratic term * \param c The coefficiont for the linear term * \param c The coefficient for the linear term * \param d The coefficiont for the constant term * \param d The coefficient for the constant term * \param numPostProcessIterations The number of iterations to increase precision of the analytical result */ */ template template int invertCubicPolynomial(SolContainer *sol, int invertCubicPolynomial(SolContainer *sol, Scalar a, Scalar a, Scalar b, Scalar c, Scalar d, Scalar b, std::size_t numPostProcessIterations = 1) Scalar c, Scalar d) { { // reduces to a quadratic polynomial // reduces to a quadratic polynomial if (a == 0) if (a == 0) ... @@ -334,7 +341,7 @@ int invertCubicPolynomial(SolContainer *sol, ... @@ -334,7 +341,7 @@ int invertCubicPolynomial(SolContainer *sol, // Choose the root that is safe against the loss of precision // Choose the root that is safe against the loss of precision // that can cause wDisc to be equal to q*q/4 despite p != 0. // that can cause wDisc to be equal to q*q/4 despite p != 0. // We do not want u to be zero in that case. Mathematically, // We do not want u to be zero in that case. Mathematically, // we are happy with either root. // we are happy with either root. const Scalar u = [&]{ const Scalar u = [&]{ return q > 0 ? cbrt(-0.5*q - sqrt(wDisc)) : cbrt(-0.5*q + sqrt(wDisc)); return q > 0 ? cbrt(-0.5*q - sqrt(wDisc)) : cbrt(-0.5*q + sqrt(wDisc)); }(); }(); ... @@ -344,7 +351,7 @@ int invertCubicPolynomial(SolContainer *sol, ... @@ -344,7 +351,7 @@ int invertCubicPolynomial(SolContainer *sol, // does not produce a division by zero. the remaining two // does not produce a division by zero. the remaining two // roots of u are rotated by +- 2/3*pi in the complex plane // roots of u are rotated by +- 2/3*pi in the complex plane // and thus not considered here // and thus not considered here invertCubicPolynomialPostProcess_(sol, 1, a, b, c, d); invertCubicPolynomialPostProcess_(sol, 1, a, b, c, d, numPostProcessIterations); return 1; return 1; } } else { // the negative discriminant case: else { // the negative discriminant case: ... @@ -404,7 +411,7 @@ int invertCubicPolynomial(SolContainer *sol, ... @@ -404,7 +411,7 @@ int invertCubicPolynomial(SolContainer *sol, // post process the obtained solution to increase numerical // post process the obtained solution to increase numerical // precision // precision invertCubicPolynomialPostProcess_(sol, 3, a, b, c, d); invertCubicPolynomialPostProcess_(sol, 3, a, b, c, d, numPostProcessIterations); // sort the result // sort the result using std::sort; using std::sort; ... ...
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!