#pragma once #include #include #include #include #include #include #include #include #include #include #include "drake/common/autodiff.h" #include "drake/common/default_scalars.h" #include "drake/common/drake_assert.h" #include "drake/common/drake_throw.h" #include "drake/common/fmt_ostream.h" #include "drake/common/symbolic/expression.h" namespace drake { /** A scalar multi-variate polynomial, modeled after the msspoly in spotless. * * Polynomial represents a list of additive Monomials, each one of which is a * product of a constant coefficient (of T, which by default is double) and any * number of distinct Terms (variables raised to positive integer powers). * * Variables are identified by integer indices rather than symbolic names, but * an automatic facility is provided to convert variable names up to four * characters into unique integers, provided those variables are named using * only lowercase letters and the "@#_." characters followed by a number. For * example, valid names include "dx4" and "m_x". * * Monomials which have the same variables and powers may be constructed but * will be automatically combined: (3 * a * b * a) + (1.5 * b * a**2) will be * reduced to (4.5 * b * a**2) internally after construction. * * Polynomials can be added, subtracted, and multiplied. They may only be * divided by scalars (of T) because Polynomials are not closed under division. * * @tparam_default_scalar */ template class Polynomial { public: DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(Polynomial); typedef unsigned int VarType; /// This should be 'unsigned int' but MSVC considers a call to std::pow(..., /// unsigned int) ambiguous because it won't cast unsigned int to int. typedef int PowerType; typedef typename Eigen::NumTraits::Real RealScalar; typedef std::complex RootType; typedef Eigen::Matrix RootsType; template struct Product { typedef decltype(static_cast(0) * static_cast(0)) type; }; /// An individual variable raised to an integer power; e.g. x**2. class Term { public: VarType var; PowerType power; bool operator==(const Term& other) const { return (var == other.var) && (power == other.power); } /// A comparison to allow std::lexicographical_compare on this class; does /// not reflect any sort of mathematical total order. bool operator<(const Term& other) const { return ((var < other.var) || ((var == other.var) && (power < other.power))); } }; /// An additive atom of a Polynomial: The product of any number of /// Terms and a coefficient. class Monomial { public: T coefficient; std::vector terms; // a list of N variable ids bool operator==(const Monomial& other) const { return (coefficient == other.coefficient) && (terms == other.terms); } /// A comparison to allow std::lexicographical_compare on this class; does /// not reflect any sort of mathematical total order. bool operator<(const Monomial& other) const { return ((coefficient < other.coefficient) || ((coefficient == other.coefficient) && (terms < other.terms))); } int GetDegree() const; int GetDegreeOf(VarType var) const; bool HasSameExponents(const Monomial& other) const; bool HasVariable(const VarType& var) const; /// Factors this by other; returns 0 iff other does not divide this. Monomial Factor(const Monomial& divisor) const; }; /// Construct the vacuous polynomial, "0". Polynomial(void) : is_univariate_(true) {} /// Construct a Polynomial of a single constant. e.g. "5". // This is required for some Eigen operations when used in a // polynomial matrix. // NOLINTNEXTLINE(runtime/explicit) This conversion is desirable. Polynomial(const T& scalar); /// Construct a Polynomial consisting of a single Monomial, e.g. "5xy**3". Polynomial(const T coeff, const std::vector& terms); /// Construct a Polynomial from a sequence of Monomials. Polynomial(typename std::vector::const_iterator start, typename std::vector::const_iterator finish); /// Constructs a polynomial consisting of a single Monomial of the variable /// named `varname1`. /// /// @note: This constructor is only provided for T = double. For the other /// cases, a user should use the constructor with two arguments below (taking /// std::string and unsigned int). If we provided this constructor for T = /// AutoDiffXd and T = symbolic::Expression, there would be compiler errors /// for `Polynomial(0)` as the following candidates are ambiguous: /// - Polynomial(const T& scalar) /// - Polynomial(const std::string& varname, const unsigned int num = 1) template explicit Polynomial( const std::enable_if_t, std::string>& varname) : Polynomial(varname, 1) { // TODO(soonho-tri): Consider deprecating this constructor to make the API // consistent for different scalar types. } /// Construct a polynomial consisting of a single Monomial of the variable /// named varname + num. Polynomial(const std::string& varname, unsigned int num); /// Construct a single Monomial of the given coefficient and variable. Polynomial(const T& coeff, const VarType& v); /// A constructor for univariate polynomials: takes a vector of coefficients /// for the x**0, x**1, x**2, x**3... Monomials. All terms are always added, /// even if a coefficient is zero. template explicit Polynomial(const Eigen::MatrixBase& coefficients) { DRAKE_THROW_UNLESS((coefficients.cols() == 1) || (coefficients.rows() == 1) || (coefficients.size() == 0)); *this = Polynomial(WithCoefficients{coefficients.template cast()}); } /// Returns the number of unique Monomials (and thus the number of /// coefficients) in this Polynomial. int GetNumberOfCoefficients() const; /** Returns the highest degree of any Monomial in this Polynomial. * * The degree of a multivariate Monomial is the product of the degrees of * each of its terms. */ int GetDegree() const; /// Returns true iff this is a sum of terms of degree 1, plus a constant. bool IsAffine() const; /// If the polynomial is "simple" -- e.g. just a single term with /// coefficient 1 -- then returns that variable; otherwise returns 0. VarType GetSimpleVariable() const; const std::vector& GetMonomials() const; VectorX GetCoefficients() const; /// Returns a set of all of the variables present in this Polynomial. std::set GetVariables() const; /** Evaluate a univariate Polynomial at a specific point. * * Evaluates a univariate Polynomial at the given x. * @throws std::exception if this Polynomial is not univariate. * * @p x may be of any type supporting the ** and + operations (which can be * different from both CoefficientsType and RealScalar). * * This method may also be used for efficient evaluation of the derivatives of * the univariate polynomial, evaluated at @p x. @p derivative_order = 0 (the * default) returns the polynomial value without differentiation. @p * derivative_order = 1 returns the first derivative, etc. * * @pre derivative_order must be non-negative. */ template typename Product::type EvaluateUnivariate( const U& x, int derivative_order = 0) const { // Note: have to remove_const because Product::type and // even Product::type returns const AutoDiff. typedef typename std::remove_const_t::type> ProductType; if (!is_univariate_) throw std::runtime_error( "this method can only be used for univariate polynomials"); DRAKE_DEMAND(derivative_order >= 0); ProductType value = 0; using std::pow; for (typename std::vector::const_iterator iter = monomials_.begin(); iter != monomials_.end(); iter++) { PowerType degree = iter->terms.empty() ? 0 : iter->terms[0].power; if (degree < derivative_order) continue; T coefficient = iter->coefficient; for (int i = 0; i < derivative_order; i++) { coefficient *= degree--; } if (degree == 0) { value += coefficient; } else if (degree == 1) { value += coefficient * x; } else { // degree > 1. value += coefficient * pow(static_cast(x), degree); } } return value; } /** Evaluate a multivariate Polynomial at a specific point. * * Evaluates a Polynomial with the given values for each variable. * @throws std::exception if the Polynomial contains variables for which * values were not provided. * * The provided values may be of any type which is std::is_arithmetic * (supporting the std::pow, *, and + operations) and need not be * CoefficientsType or RealScalar) */ template typename Product::type EvaluateMultivariate( const std::map& var_values) const { using std::pow; typedef typename std::remove_const_t< typename Product::type> ProductType; ProductType value = 0; for (const Monomial& monomial : monomials_) { ProductType monomial_value = monomial.coefficient; for (const Term& term : monomial.terms) { monomial_value *= pow(static_cast(var_values.at(term.var)), term.power); } value += monomial_value; } return value; } /** Substitute values for some but not necessarily all variables of a * Polynomial. * * Analogous to EvaluateMultivariate, but: * (1) Restricted to T, and * (2) Need not map every variable in var_values. * * Returns a Polynomial in which each variable in var_values has been * replaced with its value and constants appropriately combined. */ Polynomial EvaluatePartial( const std::map& var_values) const; /// Replaces all instances of variable orig with replacement. void Subs(const VarType& orig, const VarType& replacement); /// Replaces all instances of variable orig with replacement. Polynomial Substitute(const VarType& orig, const Polynomial& replacement) const; /** Takes the derivative of this (univariate) Polynomial. * * Returns a new Polynomial that is the derivative of this one in its sole * variable. * @throws std::exception if this Polynomial is not univariate. * * If derivative_order is given, takes the nth derivative of this * Polynomial. */ Polynomial Derivative(int derivative_order = 1) const; /** Takes the integral of this (univariate, non-constant) Polynomial. * * Returns a new Polynomial that is the indefinite integral of this one in * its sole variable. * @throws std::exception if this Polynomial is not univariate, or if it has * no variables. * * If integration_constant is given, adds that constant as the constant * term (zeroth-order coefficient) of the resulting Polynomial. */ Polynomial Integral(const T& integration_constant = 0.0) const; bool operator==(const Polynomial& other) const; Polynomial& operator+=(const Polynomial& other); Polynomial& operator-=(const Polynomial& other); Polynomial& operator*=(const Polynomial& other); Polynomial& operator+=(const T& scalar); Polynomial& operator-=(const T& scalar); Polynomial& operator*=(const T& scalar); Polynomial& operator/=(const T& scalar); const Polynomial operator+(const Polynomial& other) const; const Polynomial operator-(const Polynomial& other) const; const Polynomial operator-() const; const Polynomial operator*(const Polynomial& other) const; friend const Polynomial operator+(const Polynomial& p, const T& scalar) { Polynomial ret = p; ret += scalar; return ret; } friend const Polynomial operator+(const T& scalar, const Polynomial& p) { Polynomial ret = p; ret += scalar; return ret; } friend const Polynomial operator-(const Polynomial& p, const T& scalar) { Polynomial ret = p; ret -= scalar; return ret; } friend const Polynomial operator-(const T& scalar, const Polynomial& p) { Polynomial ret = -p; ret += scalar; return ret; } friend const Polynomial operator*(const Polynomial& p, const T& scalar) { Polynomial ret = p; ret *= scalar; return ret; } friend const Polynomial operator*(const T& scalar, const Polynomial& p) { Polynomial ret = p; ret *= scalar; return ret; } const Polynomial operator/(const T& scalar) const; /// A comparison to allow std::lexicographical_compare on this class; does /// not reflect any sort of mathematical total order. bool operator<(const Polynomial& other) const { // Just delegate to the default vector std::lexicographical_compare. return monomials_ < other.monomials_; } /** Returns the roots of this (univariate) Polynomial. * * Returns the roots of a univariate Polynomial as an Eigen column vector of * complex numbers whose components are of the RealScalar type. * @throws std::exception of this Polynomial is not univariate. */ RootsType Roots() const; /** Checks if a Polynomial is approximately equal to this one. * * Checks that every coefficient of `other` is within `tol` of the * corresponding coefficient of this Polynomial. * * Note: When `tol_type` is kRelative, if any monomials appear in `this` or * `other` but not both, then the method returns false (since the comparison * is relative to a missing zero coefficient). Use kAbsolute if you want to * ignore non-matching monomials with coefficients less than `tol`. */ boolean CoefficientsAlmostEqual( const Polynomial& other, const RealScalar& tol = 0.0, const ToleranceType& tol_type = ToleranceType::kAbsolute) const; /** Constructs a Polynomial representing the symbolic expression `e`. * Note that the ID of a variable is preserved in this translation. * * @throws std::exception if `e` is not polynomial-convertible. * @pre e.is_polynomial() is true. */ static Polynomial FromExpression(const drake::symbolic::Expression& e); // TODO(jwnimmer-tri) Rewrite this as a fmt::formatter specialization. friend std::ostream& operator<<(std::ostream& os, const Monomial& m) { // if (m.coefficient == 0) return os; bool print_star = false; if (m.coefficient == -1) { os << "-"; } else if (m.coefficient != 1 || m.terms.empty()) { os << '(' << m.coefficient << ")"; print_star = true; } for (typename std::vector::const_iterator iter = m.terms.begin(); iter != m.terms.end(); iter++) { if (print_star) os << '*'; else print_star = true; os << IdToVariableName(iter->var); if (iter->power != 1) { os << "^" << iter->power; } } return os; } // TODO(jwnimmer-tri) Rewrite this as a fmt::formatter specialization. friend std::ostream& operator<<(std::ostream& os, const Polynomial& poly) { if (poly.monomials_.empty()) { os << "0"; return os; } for (typename std::vector::const_iterator iter = poly.monomials_.begin(); iter != poly.monomials_.end(); iter++) { os << *iter; if (iter + 1 != poly.monomials_.end() && (iter + 1)->coefficient != -1) os << '+'; } return os; } //@{ /** Variable name/ID conversion facility. */ static bool IsValidVariableName(const std::string name); static VarType VariableNameToId(const std::string name, unsigned int m = 1); static std::string IdToVariableName(const VarType id); //@} template friend Polynomial pow(const Polynomial& p, typename Polynomial::PowerType n); private: // A wrapper struct to help guide constructor overload resolution. struct WithCoefficients { Eigen::Ref> value; }; explicit Polynomial(const WithCoefficients& coefficients); /// The Monomial atoms of the Polynomial. std::vector monomials_; /// True iff only 0 or 1 distinct variables appear in the Polynomial. bool is_univariate_; /// Sorts through Monomial list and merges any that have the same powers. void MakeMonomialsUnique(void); }; /** Provides power function for Polynomial. */ template Polynomial pow( const Polynomial& base, typename Polynomial::PowerType exponent) { DRAKE_DEMAND(exponent >= 0); if (exponent == 0) { return Polynomial{1.0}; } const Polynomial pow_half{pow(base, exponent / 2)}; if (exponent % 2 == 1) { return base * pow_half * pow_half; // Odd exponent case. } else { return pow_half * pow_half; // Even exponent case. } } // TODO(jwnimmer-tri) Rewrite this as a fmt::formatter specialization, // most likely just fmt_eigen without anything extra. template std::ostream& operator<<( std::ostream& os, const Eigen::Matrix, Rows, Cols>& poly_mat) { for (int i = 0; i < poly_mat.rows(); i++) { os << "[ "; for (int j = 0; j < poly_mat.cols(); j++) { os << poly_mat(i, j); if (j < (poly_mat.cols() - 1)) os << " , "; } os << " ]" << std::endl; } return os; } typedef Polynomial Polynomiald; /// A column vector of polynomials; used in several optimization classes. typedef Eigen::Matrix VectorXPoly; } // namespace drake // TODO(jwnimmer-tri) Add a real formatter and deprecate the operator<<. namespace fmt { template struct formatter> : drake::ostream_formatter {}; template <> struct formatter::Monomial> : drake::ostream_formatter {}; } // namespace fmt DRAKE_DECLARE_CLASS_TEMPLATE_INSTANTIATIONS_ON_DEFAULT_SCALARS( class drake::Polynomial)