forked from pz4kybsvg/Conception
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
535 lines
18 KiB
535 lines
18 KiB
#pragma once
|
|
|
|
#include <complex>
|
|
#include <map>
|
|
#include <random>
|
|
#include <set>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include <Eigen/Core>
|
|
#include <unsupported/Eigen/Polynomials>
|
|
|
|
#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 <typename T = double>
|
|
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<T>::Real RealScalar;
|
|
typedef std::complex<RealScalar> RootType;
|
|
typedef Eigen::Matrix<RootType, Eigen::Dynamic, 1> RootsType;
|
|
|
|
template <typename Rhs, typename Lhs>
|
|
struct Product {
|
|
typedef decltype(static_cast<Rhs>(0) * static_cast<Lhs>(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<Term> 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<Term>& terms);
|
|
|
|
/// Construct a Polynomial from a sequence of Monomials.
|
|
Polynomial(typename std::vector<Monomial>::const_iterator start,
|
|
typename std::vector<Monomial>::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<T>(0)` as the following candidates are ambiguous:
|
|
/// - Polynomial(const T& scalar)
|
|
/// - Polynomial(const std::string& varname, const unsigned int num = 1)
|
|
template <typename U = T>
|
|
explicit Polynomial(
|
|
const std::enable_if_t<std::is_same_v<U, double>, std::string>& varname)
|
|
: Polynomial<T>(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 <typename Derived>
|
|
explicit Polynomial(const Eigen::MatrixBase<Derived>& coefficients) {
|
|
DRAKE_THROW_UNLESS((coefficients.cols() == 1) ||
|
|
(coefficients.rows() == 1) ||
|
|
(coefficients.size() == 0));
|
|
*this = Polynomial(WithCoefficients{coefficients.template cast<T>()});
|
|
}
|
|
|
|
/// 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<Monomial>& GetMonomials() const;
|
|
|
|
VectorX<T> GetCoefficients() const;
|
|
|
|
/// Returns a set of all of the variables present in this Polynomial.
|
|
std::set<VarType> 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 U>
|
|
typename Product<T, U>::type EvaluateUnivariate(
|
|
const U& x, int derivative_order = 0) const {
|
|
// Note: have to remove_const because Product<AutoDiff, AutoDiff>::type and
|
|
// even Product<double, AutoDiff>::type returns const AutoDiff.
|
|
typedef typename std::remove_const_t<typename Product<T, U>::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<Monomial>::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<ProductType>(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 U>
|
|
typename Product<T, U>::type EvaluateMultivariate(
|
|
const std::map<VarType, U>& var_values) const {
|
|
using std::pow;
|
|
typedef typename std::remove_const_t<
|
|
typename Product<T, U>::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<ProductType>(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<VarType, T>& 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<T> CoefficientsAlmostEqual(
|
|
const Polynomial<T>& 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<T> 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<Term>::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<Monomial>::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 <typename U>
|
|
friend Polynomial<U> pow(const Polynomial<U>& p,
|
|
typename Polynomial<U>::PowerType n);
|
|
|
|
private:
|
|
// A wrapper struct to help guide constructor overload resolution.
|
|
struct WithCoefficients {
|
|
Eigen::Ref<const VectorX<T>> value;
|
|
};
|
|
|
|
explicit Polynomial(const WithCoefficients& coefficients);
|
|
|
|
/// The Monomial atoms of the Polynomial.
|
|
std::vector<Monomial> 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 <typename T>
|
|
Polynomial<T> pow(
|
|
const Polynomial<T>& base,
|
|
typename Polynomial<T>::PowerType exponent) {
|
|
DRAKE_DEMAND(exponent >= 0);
|
|
if (exponent == 0) {
|
|
return Polynomial<T>{1.0};
|
|
}
|
|
const Polynomial<T> 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 <typename T, int Rows, int Cols>
|
|
std::ostream& operator<<(
|
|
std::ostream& os,
|
|
const Eigen::Matrix<Polynomial<T>, 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<double> Polynomiald;
|
|
|
|
/// A column vector of polynomials; used in several optimization classes.
|
|
typedef Eigen::Matrix<Polynomiald, Eigen::Dynamic, 1> VectorXPoly;
|
|
} // namespace drake
|
|
|
|
// TODO(jwnimmer-tri) Add a real formatter and deprecate the operator<<.
|
|
namespace fmt {
|
|
template <typename T>
|
|
struct formatter<drake::Polynomial<T>>
|
|
: drake::ostream_formatter {};
|
|
template <>
|
|
struct formatter<drake::Polynomial<double>::Monomial>
|
|
: drake::ostream_formatter {};
|
|
} // namespace fmt
|
|
|
|
DRAKE_DECLARE_CLASS_TEMPLATE_INSTANTIATIONS_ON_DEFAULT_SCALARS(
|
|
class drake::Polynomial)
|