-
Notifications
You must be signed in to change notification settings - Fork 80
Add Polynomial3 class #393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
263997b
Add Polynomial3 class
hidmic 6eace76
Drop visibility attribute
hidmic ff89a91
Fix Polynomial3::Evaluate() for NaN arguments
hidmic 0108ccf
Fix Polynomial3::Minimum() implementation
hidmic 18b8ec5
Fix Polynomial3 example
hidmic 165c7d5
Simplify Polynomial3::Minimum()
hidmic 9b3c140
Merge branch 'main' into add-polynomial3-class
chapulina 2c08718
Overload Polynomial3::Minimum() to return X too
hidmic 4d8d4b1
Extend Polynomial3 unit tests
hidmic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| /* | ||
| * Copyright (C) 2022 Open Source Robotics Foundation | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| * | ||
| */ | ||
| //! [complete] | ||
| #include <iostream> | ||
| #include <ignition/math/Polynomial3.hh> | ||
| #include <ignition/math/Vector4.hh> | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| // A default constructed polynomial should have zero coefficients. | ||
| std::cout << "A default constructed polynomial is always: " | ||
| << ignition::math::Polynomial3d() << std::endl; | ||
|
|
||
| // A constant polynomial only has an independent term. | ||
| std::cout << "A constant polynomial only has an independent term: " | ||
| << ignition::math::Polynomial3d::Constant(-1.) << std::endl; | ||
|
|
||
| // A cubic polynomial may be incomplete. | ||
| const ignition::math::Polynomial3d p( | ||
| ignition::math::Vector4d(1., 0., -1., 2.)); | ||
| std::cout << "A cubic polynomial may be incomplete: " << p << std::endl; | ||
|
|
||
| // A polynomial can be evaluated. | ||
| const double x = 0.5; | ||
| std::cout << "Evaluating " << p << " at " << x | ||
| << " yields " << p(x) << std::endl; | ||
|
|
||
| // A polynomial can be queried for its minimum in a given interval, | ||
| // as well as for its global minimum (which may not always be finite). | ||
| const ignition::math::Intervald interval = | ||
| ignition::math::Intervald::Closed(-1, 1.); | ||
| std::cout << "The minimum of " << p << " in the " << interval | ||
| << " interval is " << p.Minimum(interval) << std::endl; | ||
| std::cout << "The global minimum of " << p | ||
| << " is " << p.Minimum() << std::endl; | ||
|
|
||
| const ignition::math::Polynomial3d q( | ||
| ignition::math::Vector4d(0., 1., 2., 1.)); | ||
| std::cout << "The minimum of " << q << " in the " << interval | ||
| << " interval is " << q.Minimum(interval) << std::endl; | ||
| std::cout << "The global minimum of " << q | ||
| << " is " << q.Minimum() << std::endl; | ||
|
|
||
| } | ||
| //! [complete] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,295 @@ | ||
| /* | ||
| * Copyright (C) 2022 Open Source Robotics Foundation | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| * | ||
| */ | ||
| #ifndef IGNITION_MATH_POLYNOMIAL3_HH_ | ||
| #define IGNITION_MATH_POLYNOMIAL3_HH_ | ||
|
|
||
| #include <algorithm> | ||
| #include <cmath> | ||
| #include <limits> | ||
| #include <string> | ||
| #include <utility> | ||
|
|
||
| #include <ignition/math/Interval.hh> | ||
| #include <ignition/math/Vector4.hh> | ||
| #include <ignition/math/config.hh> | ||
|
|
||
| namespace ignition | ||
| { | ||
| namespace math | ||
| { | ||
| // Inline bracket to help doxygen filtering. | ||
| inline namespace IGNITION_MATH_VERSION_NAMESPACE { | ||
| // | ||
| /// \class Polynomial3 Polynomial3.hh ignition/math/Polynomial3.hh | ||
| /// \brief The Polynomial3 class represents a cubic polynomial | ||
| /// with real coefficients p(x) = c0 x^3 + c1 x^2 + c2 x + c3. | ||
| /// ## Example | ||
| /// | ||
| /// \snippet examples/polynomial3_example.cc complete | ||
| template <typename T> | ||
| class Polynomial3 | ||
| { | ||
| /// \brief Constructor | ||
| public: Polynomial3() = default; | ||
|
|
||
| /// \brief Constructor | ||
| /// \param[in] _coeffs coefficients c0 through c3, left to right | ||
| public: explicit Polynomial3(Vector4<T> _coeffs) | ||
| : coeffs(std::move(_coeffs)) | ||
| { | ||
| } | ||
|
|
||
| /// \brief Make a constant polynomial | ||
| /// \return a p(x) = `_value` polynomial | ||
| public: static Polynomial3 Constant(T _value) | ||
| { | ||
| return Polynomial3(Vector4<T>(0., 0., 0., _value)); | ||
| } | ||
|
|
||
| /// \brief Get the polynomial coefficients | ||
| /// \return this polynomial coefficients | ||
| public: const Vector4<T> &Coeffs() const { return this->coeffs; } | ||
|
|
||
| /// \brief Evaluate the polynomial at `_x` | ||
| /// For non-finite `_x`, this function | ||
| /// computes p(z) as z tends to `_x`. | ||
| /// \param[in] _x polynomial argument | ||
| /// \return the result of evaluating p(`_x`) | ||
| public: T Evaluate(const T &_x) const | ||
| { | ||
| using std::isnan, std::isfinite; | ||
| if (isnan(_x)) | ||
| { | ||
| return _x; | ||
| } | ||
| if (!isfinite(_x)) | ||
| { | ||
| using std::abs, std::copysign; | ||
| const T epsilon = | ||
| std::numeric_limits<T>::epsilon(); | ||
| if (abs(this->coeffs[0]) >= epsilon) | ||
| { | ||
| return _x * copysign(T(1.), this->coeffs[0]); | ||
| } | ||
| if (abs(this->coeffs[1]) >= epsilon) | ||
| { | ||
| return copysign(_x, this->coeffs[1]); | ||
| } | ||
| if (abs(this->coeffs[2]) >= epsilon) | ||
| { | ||
| return _x * copysign(T(1.), this->coeffs[2]); | ||
| } | ||
| return this->coeffs[3]; | ||
| } | ||
| const T _x2 = _x * _x; | ||
| const T _x3 = _x2 * _x; | ||
|
|
||
| return (this->coeffs[0] * _x3 + this->coeffs[1] * _x2 + | ||
| this->coeffs[2] * _x + this->coeffs[3]); | ||
| } | ||
|
|
||
| /// \brief Call operator overload | ||
| /// \see Polynomial3::Evaluate() | ||
| public: T operator()(const T &_x) const | ||
| { | ||
| return this->Evaluate(_x); | ||
| } | ||
|
|
||
| /// \brief Compute polynomial minimum in an `_interval` | ||
| /// \param[in] _interval polynomial argument interval to check | ||
| /// \param[out] _xMin polynomial argument that yields minimum, | ||
| /// or NaN if the interval is empty | ||
| /// \return the polynomial minimum in the given interval, | ||
| /// or NaN if the interval is empty | ||
| public: T Minimum(const Interval<T> &_interval, T &_xMin) const | ||
| { | ||
| if (_interval.Empty()) | ||
| { | ||
| _xMin = std::numeric_limits<T>::quiet_NaN(); | ||
| return std::numeric_limits<T>::quiet_NaN(); | ||
| } | ||
| T yMin; | ||
| // For open intervals, assume continuity in the limit | ||
| const T &xLeft = _interval.LeftValue(); | ||
| const T &xRight = _interval.RightValue(); | ||
| const T yLeft = this->Evaluate(xLeft); | ||
| const T yRight = this->Evaluate(xRight); | ||
| if (yLeft <= yRight) | ||
| { | ||
| yMin = yLeft; | ||
| _xMin = xLeft; | ||
| } | ||
| else | ||
| { | ||
| yMin = yRight; | ||
| _xMin = xRight; | ||
| } | ||
| using std::abs, std::sqrt; // enable ADL | ||
| constexpr T epsilon = std::numeric_limits<T>::epsilon(); | ||
| if (abs(this->coeffs[0]) >= epsilon) | ||
| { | ||
| // Polynomial function p(x) is cubic, look | ||
| // for local minima within the given interval | ||
|
|
||
| // Find local extrema by computing the roots | ||
| // of p'(x), a quadratic polynomial function | ||
| const T a = this->coeffs[0] * T(3.); | ||
| const T b = this->coeffs[1] * T(2.); | ||
| const T c = this->coeffs[2]; | ||
|
|
||
| const T discriminant = b * b - T(4.) * a * c; | ||
| if (discriminant >= T(0.)) | ||
| { | ||
| // Roots of p'(x) are real, check local minima | ||
| const T x = (-b + sqrt(discriminant)) / (T(2.) * a); | ||
| if (_interval.Contains(x)) | ||
| { | ||
| const T y = this->Evaluate(x); | ||
| if (y < yMin) | ||
| { | ||
| _xMin = x; | ||
| yMin = y; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| else if (abs(this->coeffs[1]) >= epsilon) | ||
| { | ||
| // Polynomial function p(x) is quadratic, | ||
| // look for global minima if concave | ||
| const T a = this->coeffs[1]; | ||
| const T b = this->coeffs[2]; | ||
| if (a > T(0.)) | ||
| { | ||
| const T x = -b / (T(2.) * a); | ||
| if (_interval.Contains(x)) | ||
| { | ||
| const T y = this->Evaluate(x); | ||
| if (y < yMin) | ||
| { | ||
| _xMin = x; | ||
| yMin = y; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return yMin; | ||
| } | ||
|
|
||
| /// \brief Compute polynomial minimum in an `_interval` | ||
| /// \param[in] _interval polynomial argument interval to check | ||
| /// \return the polynomial minimum in the given interval (may | ||
| /// not be finite), or NaN if the interval is empty | ||
| public: T Minimum(const Interval<T> &_interval) const | ||
| { | ||
| T xMin; | ||
| return this->Minimum(_interval, xMin); | ||
| } | ||
|
|
||
| /// \brief Compute polynomial minimum | ||
| /// \param[out] _xMin polynomial argument that yields minimum | ||
| /// \return the polynomial minimum (may not be finite) | ||
| public: T Minimum(T &_xMin) const | ||
| { | ||
| return this->Minimum(Interval<T>::Unbounded, _xMin); | ||
| } | ||
|
|
||
| /// \brief Compute polynomial minimum | ||
| /// \return the polynomial minimum (may not be finite) | ||
| public: T Minimum() const | ||
| { | ||
| T xMin; | ||
| return this->Minimum(Interval<T>::Unbounded, xMin); | ||
| } | ||
|
|
||
| /// \brief Prints polynomial as p(`_x`) to `_out` stream | ||
| /// \param[in] _out Output stream to print to | ||
| /// \param[in] _x Argument name to be used | ||
| public: void Print(std::ostream &_out, const std::string &_x = "x") const | ||
| { | ||
| constexpr T epsilon = | ||
| std::numeric_limits<T>::epsilon(); | ||
| bool streamStarted = false; | ||
| for (size_t i = 0; i < 4; ++i) | ||
| { | ||
| using std::abs; // enable ADL | ||
| const T magnitude = abs(this->coeffs[i]); | ||
| const bool sign = this->coeffs[i] < T(0); | ||
| const int exponent = 3 - i; | ||
| if (magnitude >= epsilon) | ||
| { | ||
| if (streamStarted) | ||
| { | ||
| if (sign) | ||
| { | ||
| _out << " - "; | ||
| } | ||
| else | ||
| { | ||
| _out << " + "; | ||
| } | ||
| } | ||
| else if (sign) | ||
| { | ||
| _out << "-"; | ||
| } | ||
| if (exponent > 0) | ||
| { | ||
| if ((magnitude - T(1)) > epsilon) | ||
| { | ||
| _out << magnitude << " "; | ||
| } | ||
| _out << _x; | ||
| if (exponent > 1) | ||
| { | ||
| _out << "^" << exponent; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| _out << magnitude; | ||
| } | ||
| streamStarted = true; | ||
| } | ||
| } | ||
| if (!streamStarted) | ||
| { | ||
| _out << this->coeffs[3]; | ||
| } | ||
| } | ||
|
|
||
| /// \brief Stream insertion operator | ||
| /// \param _out output stream | ||
| /// \param _p Polynomial3 to output | ||
| /// \return the stream | ||
| public: friend std::ostream &operator<<( | ||
| std::ostream &_out, const ignition::math::Polynomial3<T> &_p) | ||
| { | ||
| _p.Print(_out, "x"); | ||
| return _out; | ||
| } | ||
|
|
||
| /// \brief Polynomial coefficients | ||
| private: Vector4<T> coeffs; | ||
| }; | ||
| using Polynomial3f = Polynomial3<float>; | ||
| using Polynomial3d = Polynomial3<double>; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #endif | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.