Loading include/usgscsm/Utilities.h +19 −6 Original line number Original line Diff line number Diff line Loading @@ -78,11 +78,24 @@ double brentRoot( std::function<double(double)> func, std::function<double(double)> func, double epsilon = 1e-10); double epsilon = 1e-10); double secantRoot( // Evaluate a polynomial function. double lowerBound, // Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2 double upperBound, double evaluatePolynomial( std::function<double(double)> func, const std::vector<double> &coeffs, double epsilon = 1e-10, double x); // Evaluate the derivative of a polynomial function. // Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2 double evaluatePolynomialDerivative( const std::vector<double> &coeffs, double x); // Find a root of a polynomial using Newton's method. // Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2 double polynomialRoot( const std::vector<double> &coeffs, double guess, double threshold = 1e-10, int maxIterations = 30); int maxIterations = 30); double computeEllipsoidElevation( double computeEllipsoidElevation( Loading src/UsgsAstroSarSensorModel.cpp +14 −26 Original line number Original line Diff line number Diff line Loading @@ -426,29 +426,17 @@ double UsgsAstroSarSensorModel::slantRangeToGroundRange( std::vector<double> coeffs = getRangeCoefficients(time); std::vector<double> coeffs = getRangeCoefficients(time); // Calculates the ground range from the slant range. std::function<double(double)> slantRangeToGroundRangeFunction = [this, coeffs, slantRange](double groundRange){ return slantRange - groundRangeToSlantRange(groundRange, coeffs); }; // Need to come up with an initial guess when solving for ground // Need to come up with an initial guess when solving for ground // range given slant range. Compute the ground range at the // range given slant range. Naively use the middle of the image. // near and far edges of the image by evaluating the sample-to- double guess = 0.5 * m_nSamples * m_scaledPixelWidth; // ground-range equation: groundRange=(sample-1)*scaled_pixel_width // at the edges of the image. We also need to add some padding to // allow for solving for coordinates that are slightly outside of // the actual image area. Use sample=-0.25*image_samples and // sample=1.25*image_samples. double minGroundRangeGuess = (-0.25 * m_nSamples - 1.0) * m_scaledPixelWidth; double maxGroundRangeGuess = (1.25 * m_nSamples - 1.0) * m_scaledPixelWidth; // Tolerance to 1/20th of a pixel for now. // Tolerance to 1/20th of a pixel for now. return secantRoot(minGroundRangeGuess, maxGroundRangeGuess, slantRangeToGroundRangeFunction, tolerance); coeffs[0] -= slantRange; return polynomialRoot(coeffs, guess, tolerance); } } double UsgsAstroSarSensorModel::groundRangeToSlantRange(double groundRange, const std::vector<double> &coeffs) const { double UsgsAstroSarSensorModel::groundRangeToSlantRange(double groundRange, const std::vector<double> &coeffs) const { return coeffs[0] + groundRange * (coeffs[1] + groundRange * (coeffs[2] + groundRange * coeffs[3])); return evaluatePolynomial(coeffs, groundRange); } } Loading src/Utilities.cpp +108 −113 Original line number Original line Diff line number Diff line Loading @@ -279,8 +279,7 @@ void lagrangeInterp( } } } } double brentRoot( double brentRoot(double lowerBound, double lowerBound, double upperBound, double upperBound, std::function<double(double)> func, std::function<double(double)> func, double epsilon) { double epsilon) { Loading Loading @@ -347,61 +346,57 @@ double brentRoot( return nextPoint; return nextPoint; } } double secantRoot(double lowerBound, double upperBound, std::function<double(double)> func, double evaluatePolynomial(const std::vector<double> &coeffs, double epsilon, int maxIters) { double x) { bool found = false; if (coeffs.empty()) { throw std::invalid_argument("Polynomial coeffs must be non-empty."); double x0 = lowerBound; double x1 = upperBound; double f0 = func(x0); double f1 = func(x1); double diff = 0; double x2 = 0; double f2 = 0; // Make sure we bound the root (f = 0.0) if (f0 * f1 > 0.0) { throw std::invalid_argument("Function values at the boundaries have the same sign [secantRoot]."); } // Order the bounds if (f1 < f0) { std::swap(x0, x1); std::swap(f0, f1); } } auto revIt = coeffs.crbegin(); for (int iteration=0; iteration < maxIters; iteration++) { double value = *revIt; x2 = x1 - f1 * (x1 - x0)/(f1 - f0); ++revIt; f2 = func(x2); for (; revIt != coeffs.crend(); ++revIt) { value *= x; // Update the bounds for the next iteration value += *revIt; if (f2 < 0.0) { diff = x1 - x2; x1 = x2; f1 = f2; } } else { return value; diff = x0 - x2; x0 = x2; f0 = f2; } } // Check to see if we're done double evaluatePolynomialDerivative(const std::vector<double> &coeffs, if ((fabs(diff) <= epsilon) || (f2 == 0.0) ) { double x) { found = true; if (coeffs.empty()) { break; throw std::invalid_argument("Polynomial coeffs must be non-empty."); } int i = coeffs.size() - 1; double value = i * coeffs[i]; --i; for (; i > 0; --i) { value *= x; value += i * coeffs[i]; } } return value; } } if (found) { double polynomialRoot(const std::vector<double> &coeffs, return x2; double guess, double threshold, int maxIterations) { double root = guess; double polyValue = evaluatePolynomial(coeffs, root); double polyDeriv = 0.0; for (int iteration = 0; iteration < maxIterations+1; iteration++) { if (fabs(polyValue) < threshold) { return root; } } else { polyDeriv = evaluatePolynomialDerivative(coeffs, root); throw csm::Error( if (fabs(polyDeriv) < 1e-15) { csm::Error::UNKNOWN_ERROR, throw std::invalid_argument("Derivative at guess (" + "Could not find a root of the function using the secant method", std::to_string(guess) + ") is too close to 0."); "secantRoot"); } root -= polyValue / polyDeriv; polyValue = evaluatePolynomial(coeffs, root); } } throw std::invalid_argument("Root finder did not converge after " + std::to_string(maxIterations) + " iterations"); } } double computeEllipsoidElevation( double computeEllipsoidElevation( Loading tests/UtilitiesTests.cpp +19 −7 Original line number Original line Diff line number Diff line Loading @@ -350,13 +350,25 @@ TEST(UtilitiesTests, brentRoot) { EXPECT_THROW(brentRoot(-3.0, 3.0, testPoly), std::invalid_argument); EXPECT_THROW(brentRoot(-3.0, 3.0, testPoly), std::invalid_argument); } } TEST(UtilitiesTests, secantRoot) { TEST(UtilitiesTests, polynomialEval) { std::function<double(double)> testPoly = [](double x) { std::vector<double> coeffs = {-12.0, 4.0, -3.0, 1.0}; return (x - 2) * (x + 1) * (x + 7); EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, -1.0), -20.0); }; EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, -1.0), 13.0); EXPECT_NEAR(secantRoot(1.0, 3.0, testPoly, 1e-10), 2.0, 1e-10); EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 0.0), -12.0); EXPECT_NEAR(secantRoot(0.0, -3.0, testPoly, 1e-10), -1.0, 1e-10); EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, 0.0), 4.0); EXPECT_THROW(secantRoot(-1000.0, 1000.0, testPoly), csm::Error); EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 2.0), -8.0); EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, 2.0), 4.0); EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 3.5), 8.125); EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, 3.5), 19.75); EXPECT_THROW(evaluatePolynomial(std::vector<double>(), 0.0), std::invalid_argument); EXPECT_THROW(evaluatePolynomialDerivative(std::vector<double>(), 0.0), std::invalid_argument); } TEST(UtilitiesTests, polynomialRoot) { std::vector<double> oneRootCoeffs = {-12.0, 4.0, -3.0, 1.0}; // roots are 3, +-2i std::vector<double> noRootCoeffs = {4.0, 0.0, 1.0}; // roots are +-2i EXPECT_NEAR(polynomialRoot(oneRootCoeffs, 0.0), 3.0, 1e-10); EXPECT_THROW(polynomialRoot(noRootCoeffs, 0.0), std::invalid_argument); } } TEST(UtilitiesTests, vectorProduct) { TEST(UtilitiesTests, vectorProduct) { Loading Loading
include/usgscsm/Utilities.h +19 −6 Original line number Original line Diff line number Diff line Loading @@ -78,11 +78,24 @@ double brentRoot( std::function<double(double)> func, std::function<double(double)> func, double epsilon = 1e-10); double epsilon = 1e-10); double secantRoot( // Evaluate a polynomial function. double lowerBound, // Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2 double upperBound, double evaluatePolynomial( std::function<double(double)> func, const std::vector<double> &coeffs, double epsilon = 1e-10, double x); // Evaluate the derivative of a polynomial function. // Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2 double evaluatePolynomialDerivative( const std::vector<double> &coeffs, double x); // Find a root of a polynomial using Newton's method. // Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2 double polynomialRoot( const std::vector<double> &coeffs, double guess, double threshold = 1e-10, int maxIterations = 30); int maxIterations = 30); double computeEllipsoidElevation( double computeEllipsoidElevation( Loading
src/UsgsAstroSarSensorModel.cpp +14 −26 Original line number Original line Diff line number Diff line Loading @@ -426,29 +426,17 @@ double UsgsAstroSarSensorModel::slantRangeToGroundRange( std::vector<double> coeffs = getRangeCoefficients(time); std::vector<double> coeffs = getRangeCoefficients(time); // Calculates the ground range from the slant range. std::function<double(double)> slantRangeToGroundRangeFunction = [this, coeffs, slantRange](double groundRange){ return slantRange - groundRangeToSlantRange(groundRange, coeffs); }; // Need to come up with an initial guess when solving for ground // Need to come up with an initial guess when solving for ground // range given slant range. Compute the ground range at the // range given slant range. Naively use the middle of the image. // near and far edges of the image by evaluating the sample-to- double guess = 0.5 * m_nSamples * m_scaledPixelWidth; // ground-range equation: groundRange=(sample-1)*scaled_pixel_width // at the edges of the image. We also need to add some padding to // allow for solving for coordinates that are slightly outside of // the actual image area. Use sample=-0.25*image_samples and // sample=1.25*image_samples. double minGroundRangeGuess = (-0.25 * m_nSamples - 1.0) * m_scaledPixelWidth; double maxGroundRangeGuess = (1.25 * m_nSamples - 1.0) * m_scaledPixelWidth; // Tolerance to 1/20th of a pixel for now. // Tolerance to 1/20th of a pixel for now. return secantRoot(minGroundRangeGuess, maxGroundRangeGuess, slantRangeToGroundRangeFunction, tolerance); coeffs[0] -= slantRange; return polynomialRoot(coeffs, guess, tolerance); } } double UsgsAstroSarSensorModel::groundRangeToSlantRange(double groundRange, const std::vector<double> &coeffs) const { double UsgsAstroSarSensorModel::groundRangeToSlantRange(double groundRange, const std::vector<double> &coeffs) const { return coeffs[0] + groundRange * (coeffs[1] + groundRange * (coeffs[2] + groundRange * coeffs[3])); return evaluatePolynomial(coeffs, groundRange); } } Loading
src/Utilities.cpp +108 −113 Original line number Original line Diff line number Diff line Loading @@ -279,8 +279,7 @@ void lagrangeInterp( } } } } double brentRoot( double brentRoot(double lowerBound, double lowerBound, double upperBound, double upperBound, std::function<double(double)> func, std::function<double(double)> func, double epsilon) { double epsilon) { Loading Loading @@ -347,61 +346,57 @@ double brentRoot( return nextPoint; return nextPoint; } } double secantRoot(double lowerBound, double upperBound, std::function<double(double)> func, double evaluatePolynomial(const std::vector<double> &coeffs, double epsilon, int maxIters) { double x) { bool found = false; if (coeffs.empty()) { throw std::invalid_argument("Polynomial coeffs must be non-empty."); double x0 = lowerBound; double x1 = upperBound; double f0 = func(x0); double f1 = func(x1); double diff = 0; double x2 = 0; double f2 = 0; // Make sure we bound the root (f = 0.0) if (f0 * f1 > 0.0) { throw std::invalid_argument("Function values at the boundaries have the same sign [secantRoot]."); } // Order the bounds if (f1 < f0) { std::swap(x0, x1); std::swap(f0, f1); } } auto revIt = coeffs.crbegin(); for (int iteration=0; iteration < maxIters; iteration++) { double value = *revIt; x2 = x1 - f1 * (x1 - x0)/(f1 - f0); ++revIt; f2 = func(x2); for (; revIt != coeffs.crend(); ++revIt) { value *= x; // Update the bounds for the next iteration value += *revIt; if (f2 < 0.0) { diff = x1 - x2; x1 = x2; f1 = f2; } } else { return value; diff = x0 - x2; x0 = x2; f0 = f2; } } // Check to see if we're done double evaluatePolynomialDerivative(const std::vector<double> &coeffs, if ((fabs(diff) <= epsilon) || (f2 == 0.0) ) { double x) { found = true; if (coeffs.empty()) { break; throw std::invalid_argument("Polynomial coeffs must be non-empty."); } int i = coeffs.size() - 1; double value = i * coeffs[i]; --i; for (; i > 0; --i) { value *= x; value += i * coeffs[i]; } } return value; } } if (found) { double polynomialRoot(const std::vector<double> &coeffs, return x2; double guess, double threshold, int maxIterations) { double root = guess; double polyValue = evaluatePolynomial(coeffs, root); double polyDeriv = 0.0; for (int iteration = 0; iteration < maxIterations+1; iteration++) { if (fabs(polyValue) < threshold) { return root; } } else { polyDeriv = evaluatePolynomialDerivative(coeffs, root); throw csm::Error( if (fabs(polyDeriv) < 1e-15) { csm::Error::UNKNOWN_ERROR, throw std::invalid_argument("Derivative at guess (" + "Could not find a root of the function using the secant method", std::to_string(guess) + ") is too close to 0."); "secantRoot"); } root -= polyValue / polyDeriv; polyValue = evaluatePolynomial(coeffs, root); } } throw std::invalid_argument("Root finder did not converge after " + std::to_string(maxIterations) + " iterations"); } } double computeEllipsoidElevation( double computeEllipsoidElevation( Loading
tests/UtilitiesTests.cpp +19 −7 Original line number Original line Diff line number Diff line Loading @@ -350,13 +350,25 @@ TEST(UtilitiesTests, brentRoot) { EXPECT_THROW(brentRoot(-3.0, 3.0, testPoly), std::invalid_argument); EXPECT_THROW(brentRoot(-3.0, 3.0, testPoly), std::invalid_argument); } } TEST(UtilitiesTests, secantRoot) { TEST(UtilitiesTests, polynomialEval) { std::function<double(double)> testPoly = [](double x) { std::vector<double> coeffs = {-12.0, 4.0, -3.0, 1.0}; return (x - 2) * (x + 1) * (x + 7); EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, -1.0), -20.0); }; EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, -1.0), 13.0); EXPECT_NEAR(secantRoot(1.0, 3.0, testPoly, 1e-10), 2.0, 1e-10); EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 0.0), -12.0); EXPECT_NEAR(secantRoot(0.0, -3.0, testPoly, 1e-10), -1.0, 1e-10); EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, 0.0), 4.0); EXPECT_THROW(secantRoot(-1000.0, 1000.0, testPoly), csm::Error); EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 2.0), -8.0); EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, 2.0), 4.0); EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 3.5), 8.125); EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, 3.5), 19.75); EXPECT_THROW(evaluatePolynomial(std::vector<double>(), 0.0), std::invalid_argument); EXPECT_THROW(evaluatePolynomialDerivative(std::vector<double>(), 0.0), std::invalid_argument); } TEST(UtilitiesTests, polynomialRoot) { std::vector<double> oneRootCoeffs = {-12.0, 4.0, -3.0, 1.0}; // roots are 3, +-2i std::vector<double> noRootCoeffs = {4.0, 0.0, 1.0}; // roots are +-2i EXPECT_NEAR(polynomialRoot(oneRootCoeffs, 0.0), 3.0, 1e-10); EXPECT_THROW(polynomialRoot(noRootCoeffs, 0.0), std::invalid_argument); } } TEST(UtilitiesTests, vectorProduct) { TEST(UtilitiesTests, vectorProduct) { Loading