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.
623 lines
22 KiB
623 lines
22 KiB
#include "drake/common/polynomial.h"
|
|
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <map>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
#include <Eigen/Dense>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "drake/common/symbolic/expression.h"
|
|
#include "drake/common/test_utilities/eigen_matrix_compare.h"
|
|
#include "drake/common/test_utilities/expect_no_throw.h"
|
|
#include "drake/common/test_utilities/random_polynomial_matrix.h"
|
|
|
|
using Eigen::VectorXd;
|
|
using std::default_random_engine;
|
|
using std::map;
|
|
using std::runtime_error;
|
|
using std::uniform_int_distribution;
|
|
using std::uniform_real_distribution;
|
|
using std::vector;
|
|
|
|
namespace drake {
|
|
namespace {
|
|
|
|
using std::pow;
|
|
|
|
template <typename T>
|
|
void testIntegralAndDerivative() {
|
|
VectorXd coefficients = VectorXd::Random(5);
|
|
Polynomial<T> poly(coefficients);
|
|
|
|
EXPECT_TRUE(CompareMatrices(poly.GetCoefficients(),
|
|
poly.Derivative(0).GetCoefficients(), 1e-14,
|
|
MatrixCompareType::absolute));
|
|
|
|
const T x{1.32};
|
|
Polynomial<T> first_derivative = poly.Derivative(1);
|
|
EXPECT_NEAR(poly.EvaluateUnivariate(x, 1),
|
|
first_derivative.EvaluateUnivariate(x), 1e-14);
|
|
|
|
Polynomial<T> third_derivative = poly.Derivative(3);
|
|
EXPECT_NEAR(poly.EvaluateUnivariate(x, 3),
|
|
third_derivative.EvaluateUnivariate(x), 1e-14);
|
|
|
|
Polynomial<T> third_derivative_check =
|
|
poly.Derivative().Derivative().Derivative();
|
|
|
|
EXPECT_TRUE(CompareMatrices(third_derivative.GetCoefficients(),
|
|
third_derivative_check.GetCoefficients(), 1e-14,
|
|
MatrixCompareType::absolute));
|
|
|
|
Polynomial<T> tenth_derivative = poly.Derivative(10);
|
|
|
|
EXPECT_TRUE(CompareMatrices(tenth_derivative.GetCoefficients(),
|
|
VectorXd::Zero(1), 1e-14,
|
|
MatrixCompareType::absolute));
|
|
|
|
Polynomial<T> integral = poly.Integral(0.0);
|
|
Polynomial<T> poly_back = integral.Derivative();
|
|
|
|
EXPECT_TRUE(CompareMatrices(poly_back.GetCoefficients(),
|
|
poly.GetCoefficients(), 1e-14,
|
|
MatrixCompareType::absolute));
|
|
}
|
|
|
|
template <typename T>
|
|
void testOperators() {
|
|
int max_num_coefficients = 6;
|
|
int num_tests = 10;
|
|
default_random_engine generator;
|
|
std::uniform_int_distribution<> int_distribution(1, max_num_coefficients);
|
|
uniform_real_distribution<double> uniform;
|
|
|
|
for (int i = 0; i < num_tests; ++i) {
|
|
VectorXd coeff1 = VectorXd::Random(int_distribution(generator));
|
|
Polynomial<T> poly1(coeff1);
|
|
|
|
VectorXd coeff2 = VectorXd::Random(int_distribution(generator));
|
|
Polynomial<T> poly2(coeff2);
|
|
|
|
double scalar = uniform(generator);
|
|
|
|
Polynomial<T> sum = poly1 + poly2;
|
|
Polynomial<T> difference = poly2 - poly1;
|
|
Polynomial<T> product = poly1 * poly2;
|
|
Polynomial<T> poly1_plus_scalar = poly1 + scalar;
|
|
Polynomial<T> poly1_minus_scalar = poly1 - scalar;
|
|
Polynomial<T> poly1_scaled = poly1 * scalar;
|
|
Polynomial<T> poly1_div = poly1 / scalar;
|
|
Polynomial<T> poly1_times_poly1 = poly1;
|
|
const Polynomial<T> pow_poly1_3{pow(poly1, 3)};
|
|
const Polynomial<T> pow_poly1_4{pow(poly1, 4)};
|
|
const Polynomial<T> pow_poly1_10{pow(poly1, 10)};
|
|
poly1_times_poly1 *= poly1_times_poly1;
|
|
|
|
double t = uniform(generator);
|
|
EXPECT_NEAR(sum.EvaluateUnivariate(t),
|
|
poly1.EvaluateUnivariate(t) + poly2.EvaluateUnivariate(t),
|
|
1e-8);
|
|
EXPECT_NEAR(difference.EvaluateUnivariate(t),
|
|
poly2.EvaluateUnivariate(t) - poly1.EvaluateUnivariate(t),
|
|
1e-8);
|
|
EXPECT_NEAR(product.EvaluateUnivariate(t),
|
|
poly1.EvaluateUnivariate(t) * poly2.EvaluateUnivariate(t),
|
|
1e-8);
|
|
EXPECT_NEAR(poly1_plus_scalar.EvaluateUnivariate(t),
|
|
poly1.EvaluateUnivariate(t) + scalar, 1e-8);
|
|
EXPECT_NEAR(poly1_minus_scalar.EvaluateUnivariate(t),
|
|
poly1.EvaluateUnivariate(t) - scalar, 1e-8);
|
|
EXPECT_NEAR(poly1_scaled.EvaluateUnivariate(t),
|
|
poly1.EvaluateUnivariate(t) * scalar, 1e-8);
|
|
EXPECT_NEAR(poly1_div.EvaluateUnivariate(t),
|
|
poly1.EvaluateUnivariate(t) / scalar, 1e-8);
|
|
EXPECT_NEAR(poly1_times_poly1.EvaluateUnivariate(t),
|
|
poly1.EvaluateUnivariate(t) * poly1.EvaluateUnivariate(t),
|
|
1e-8);
|
|
EXPECT_NEAR(pow_poly1_3.EvaluateUnivariate(t),
|
|
pow(poly1.EvaluateUnivariate(t), 3), 1e-8);
|
|
EXPECT_NEAR(pow_poly1_4.EvaluateUnivariate(t),
|
|
pow(poly1.EvaluateUnivariate(t), 4), 1e-8);
|
|
EXPECT_NEAR(pow_poly1_10.EvaluateUnivariate(t),
|
|
pow(poly1.EvaluateUnivariate(t), 10), 1e-8);
|
|
EXPECT_NEAR(pow_poly1_3.EvaluateUnivariate(t) *
|
|
pow_poly1_4.EvaluateUnivariate(t) *
|
|
pow_poly1_3.EvaluateUnivariate(t),
|
|
pow_poly1_10.EvaluateUnivariate(t), 1e-8);
|
|
|
|
// Check the '==' operator.
|
|
EXPECT_TRUE(poly1 + poly2 == sum);
|
|
EXPECT_FALSE(poly1 == sum);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void testRoots() {
|
|
int max_num_coefficients = 6;
|
|
default_random_engine generator;
|
|
std::uniform_int_distribution<> int_distribution(1, max_num_coefficients);
|
|
|
|
int num_tests = 50;
|
|
for (int i = 0; i < num_tests; ++i) {
|
|
VectorXd coeffs = VectorXd::Random(int_distribution(generator));
|
|
Polynomial<T> poly(coeffs);
|
|
auto roots = poly.Roots();
|
|
EXPECT_EQ(roots.rows(), poly.GetDegree());
|
|
for (int k = 0; k < roots.size(); k++) {
|
|
auto value = poly.EvaluateUnivariate(roots[k]);
|
|
EXPECT_NEAR(std::abs(value), 0.0, 1e-8);
|
|
}
|
|
}
|
|
}
|
|
|
|
void testEvalType() {
|
|
int max_num_coefficients = 6;
|
|
default_random_engine generator;
|
|
std::uniform_int_distribution<> int_distribution(1, max_num_coefficients);
|
|
VectorXd coeffs = VectorXd::Random(int_distribution(generator));
|
|
Polynomial<double> poly(coeffs);
|
|
|
|
auto valueIntInput = poly.EvaluateUnivariate(1);
|
|
EXPECT_EQ(typeid(decltype(valueIntInput)), typeid(double));
|
|
|
|
auto valueComplexInput =
|
|
poly.EvaluateUnivariate(std::complex<double>(1.0, 2.0));
|
|
EXPECT_EQ(typeid(decltype(valueComplexInput)), typeid(std::complex<double>));
|
|
}
|
|
|
|
template <typename T>
|
|
void testPolynomialMatrix() {
|
|
int max_matrix_rows_cols = 7;
|
|
int num_coefficients = 6;
|
|
default_random_engine generator;
|
|
|
|
uniform_int_distribution<> matrix_size_distribution(1, max_matrix_rows_cols);
|
|
int rows_A = matrix_size_distribution(generator);
|
|
int cols_A = matrix_size_distribution(generator);
|
|
int rows_B = cols_A;
|
|
int cols_B = matrix_size_distribution(generator);
|
|
|
|
auto A = test::RandomPolynomialMatrix<T>(num_coefficients,
|
|
rows_A, cols_A);
|
|
auto B = test::RandomPolynomialMatrix<T>(num_coefficients,
|
|
rows_B, cols_B);
|
|
auto C = test::RandomPolynomialMatrix<T>(num_coefficients,
|
|
rows_A, cols_A);
|
|
auto product = A * B;
|
|
auto sum = A + C;
|
|
|
|
uniform_real_distribution<double> uniform;
|
|
for (int row = 0; row < A.rows(); ++row) {
|
|
for (int col = 0; col < A.cols(); ++col) {
|
|
double t = uniform(generator);
|
|
EXPECT_NEAR(sum(row, col).evaluateUnivariate(t),
|
|
A(row, col).evaluateUnivariate(t) +
|
|
C(row, col).evaluateUnivariate(t), 1e-8);
|
|
|
|
double expected_product = 0.0;
|
|
for (int i = 0; i < A.cols(); ++i) {
|
|
expected_product += A(row, i).evaluateUnivariate(t) *
|
|
B(i, col).evaluateUnivariate(t);
|
|
}
|
|
EXPECT_NEAR(product(row, col).evaluateUnivariate(t),
|
|
expected_product, 1e-8);
|
|
}
|
|
}
|
|
|
|
C.setZero(); // this was a problem before
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, CoefficientsConstructor) {
|
|
const VectorXd coefficients = Eigen::Vector3d(0.25, 0.0, 2.0);
|
|
Polynomial<double> dut(coefficients);
|
|
EXPECT_TRUE(CompareMatrices(dut.GetCoefficients(), coefficients));
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, CoefficientsConstructorEmpty) {
|
|
// An empty coefficients vector is promoted to a single zero.
|
|
const VectorXd coefficients;
|
|
Polynomial<double> dut(coefficients);
|
|
const VectorXd expected = VectorXd::Constant(1, 0.0);
|
|
EXPECT_TRUE(CompareMatrices(dut.GetCoefficients(), expected));
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, CoefficientsConstructorNoMatrix) {
|
|
// Non-vector matrices are rejected.
|
|
const Eigen::MatrixXd coefficients = Eigen::MatrixXd::Zero(3, 5);
|
|
EXPECT_THROW(Polynomial<double>{coefficients}, std::exception);
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, IntegralAndDerivative) {
|
|
testIntegralAndDerivative<double>();
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, TestMakeMonomialsUnique) {
|
|
Eigen::Vector2d coefficients(1, 2);
|
|
Polynomial<double> poly(coefficients);
|
|
const auto poly_squared = poly * poly;
|
|
EXPECT_EQ(poly_squared.GetNumberOfCoefficients(), 3);
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, Operators) { testOperators<double>(); }
|
|
|
|
GTEST_TEST(PolynomialTest, Roots) { testRoots<double>(); }
|
|
|
|
GTEST_TEST(PolynomialTest, EvalType) { testEvalType(); }
|
|
|
|
GTEST_TEST(PolynomialTest, IsAffine) {
|
|
Polynomiald x("x");
|
|
Polynomiald y("y");
|
|
|
|
EXPECT_TRUE(x.IsAffine());
|
|
EXPECT_TRUE(y.IsAffine());
|
|
EXPECT_TRUE((2 + x).IsAffine());
|
|
EXPECT_TRUE((2 * x).IsAffine());
|
|
EXPECT_FALSE((x * x).IsAffine());
|
|
EXPECT_FALSE((x * y).IsAffine());
|
|
EXPECT_TRUE((x + y).IsAffine());
|
|
EXPECT_TRUE((2 + x + y).IsAffine());
|
|
EXPECT_TRUE((2 + (2 * x) + y).IsAffine());
|
|
EXPECT_FALSE((2 + (y * x) + y).IsAffine());
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, VariableIdGeneration) {
|
|
// Probe the outer edge cases of variable ID generation.
|
|
|
|
// There is no documented maximum ID, but empirically it is 2325. What we
|
|
// really care about here is just that there is some value below which it
|
|
// succeeds and above which it fails.
|
|
static const int kMaxId = 2325;
|
|
|
|
DRAKE_EXPECT_NO_THROW(Polynomial<double>("x", kMaxId));
|
|
DRAKE_EXPECT_NO_THROW(Polynomial<double>("zzzz", 1));
|
|
DRAKE_EXPECT_NO_THROW(Polynomial<double>("zzzz", kMaxId));
|
|
EXPECT_THROW(Polynomial<double>("!"),
|
|
std::runtime_error); // Illegal character.
|
|
EXPECT_THROW(Polynomial<double>("zzzz@"),
|
|
std::runtime_error); // Illegal length.
|
|
EXPECT_THROW(Polynomial<double>("z", 0),
|
|
std::runtime_error); // Illegal ID.
|
|
EXPECT_THROW(Polynomial<double>("z", kMaxId + 1),
|
|
std::runtime_error); // Illegal ID.
|
|
|
|
// Test that ID generation round-trips correctly.
|
|
std::stringstream test_stream;
|
|
test_stream << Polynomial<double>("x", 1);
|
|
std::string result;
|
|
test_stream >> result;
|
|
EXPECT_EQ(result, "x1");
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, GetVariables) {
|
|
Polynomiald x = Polynomiald("x");
|
|
Polynomiald::VarType x_var = x.GetSimpleVariable();
|
|
Polynomiald y = Polynomiald("y");
|
|
Polynomiald::VarType y_var = y.GetSimpleVariable();
|
|
Polynomiald z = Polynomiald("z");
|
|
Polynomiald::VarType z_var = z.GetSimpleVariable();
|
|
|
|
EXPECT_TRUE(x.GetVariables().count(x_var));
|
|
EXPECT_FALSE(x.GetVariables().count(y_var));
|
|
|
|
EXPECT_FALSE(Polynomiald().GetVariables().count(x_var));
|
|
|
|
EXPECT_TRUE((x + x).GetVariables().count(x_var));
|
|
|
|
EXPECT_TRUE((x + y).GetVariables().count(x_var));
|
|
EXPECT_TRUE((x + y).GetVariables().count(y_var));
|
|
|
|
EXPECT_TRUE((x * y * y + z).GetVariables().count(x_var));
|
|
EXPECT_TRUE((x * y * y + z).GetVariables().count(y_var));
|
|
EXPECT_TRUE((x * y * y + z).GetVariables().count(z_var));
|
|
|
|
EXPECT_FALSE(x.Derivative().GetVariables().count(x_var));
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, Simplification) {
|
|
Polynomiald x = Polynomiald("x");
|
|
Polynomiald y = Polynomiald("y");
|
|
|
|
{ // Test duplicate monomials.
|
|
std::stringstream test_stream;
|
|
test_stream << ((x * y) + (x * y));
|
|
std::string result;
|
|
test_stream >> result;
|
|
EXPECT_EQ(result, "(2)*x1*y1");
|
|
}
|
|
|
|
{ // Test monomials that are duplicates under commutativity.
|
|
std::stringstream test_stream;
|
|
test_stream << ((x * y) + (y * x));
|
|
std::string result;
|
|
test_stream >> result;
|
|
EXPECT_EQ(result, "(2)*x1*y1");
|
|
}
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, MonomialFactor) {
|
|
Polynomiald x = Polynomiald("x");
|
|
Polynomiald y = Polynomiald("y");
|
|
|
|
// "m_" prefix denotes monomial.
|
|
Polynomiald::Monomial m_one = Polynomiald(1).GetMonomials()[0];
|
|
Polynomiald::Monomial m_two = Polynomiald(2).GetMonomials()[0];
|
|
Polynomiald::Monomial m_x = x.GetMonomials()[0];
|
|
Polynomiald::Monomial m_y = y.GetMonomials()[0];
|
|
Polynomiald::Monomial m_2x = (x * 2).GetMonomials()[0];
|
|
Polynomiald::Monomial m_x2 = (x * x).GetMonomials()[0];
|
|
Polynomiald::Monomial m_x2y = (x * x * y).GetMonomials()[0];
|
|
|
|
// Expect failures
|
|
EXPECT_EQ(m_x.Factor(m_y).coefficient, 0);
|
|
EXPECT_EQ(m_x.Factor(m_x2).coefficient, 0);
|
|
|
|
// Expect successes
|
|
EXPECT_EQ(m_x.Factor(m_x), m_one);
|
|
EXPECT_EQ(m_2x.Factor(m_x), m_two);
|
|
EXPECT_EQ(m_x2.Factor(m_x), m_x);
|
|
EXPECT_EQ(m_x2y.Factor(m_x2), m_y);
|
|
EXPECT_EQ(m_x2y.Factor(m_y), m_x2);
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, MultivariateValue) {
|
|
Polynomiald x = Polynomiald("x");
|
|
Polynomiald y = Polynomiald("y");
|
|
const Polynomiald p{x * x + 2 * x * y + y * y + 2};
|
|
const Polynomiald pow_p_2{pow(p, 2)};
|
|
const Polynomiald pow_p_3{pow(p, 3)};
|
|
const Polynomiald pow_p_7{pow(p, 7)};
|
|
const std::map<Polynomiald::VarType, double> eval_point = {
|
|
{x.GetSimpleVariable(), 1},
|
|
{y.GetSimpleVariable(), 2}};
|
|
EXPECT_EQ((x * x + y).EvaluateMultivariate(eval_point), 3);
|
|
EXPECT_EQ((2 * x * x + y).EvaluateMultivariate(eval_point), 4);
|
|
EXPECT_EQ((x * x + 2 * y).EvaluateMultivariate(eval_point), 5);
|
|
EXPECT_EQ((x * x + x * y).EvaluateMultivariate(eval_point), 3);
|
|
EXPECT_NEAR(pow_p_2.EvaluateMultivariate(eval_point),
|
|
pow(p.EvaluateMultivariate(eval_point), 2), 1e-8);
|
|
EXPECT_NEAR(pow_p_3.EvaluateMultivariate(eval_point),
|
|
pow(p.EvaluateMultivariate(eval_point), 3), 1e-8);
|
|
EXPECT_NEAR(pow_p_7.EvaluateMultivariate(eval_point),
|
|
pow(p.EvaluateMultivariate(eval_point), 7), 1e-8);
|
|
EXPECT_NEAR((pow_p_2 * pow_p_3 * pow_p_2).EvaluateMultivariate(eval_point),
|
|
pow_p_7.EvaluateMultivariate(eval_point), 1e-8);
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, Conversion) {
|
|
// Confirm that these conversions compile okay.
|
|
Polynomial<double> x(1.0);
|
|
Polynomial<double> y = 2.0;
|
|
Polynomial<double> z = 3;
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, EvaluatePartial) {
|
|
Polynomiald x = Polynomiald("x");
|
|
Polynomiald y = Polynomiald("y");
|
|
Polynomiald dut = (5 * x * x * x) + (3 * x * y) + (2 * y) + 1;
|
|
|
|
const std::map<Polynomiald::VarType, double> eval_point_null;
|
|
const std::map<Polynomiald::VarType, double> eval_point_x = {
|
|
{x.GetSimpleVariable(), 7}};
|
|
const std::map<Polynomiald::VarType, double> eval_point_y = {
|
|
{y.GetSimpleVariable(), 11}};
|
|
const std::map<Polynomiald::VarType, double> eval_point_xy = {
|
|
{x.GetSimpleVariable(), 7},
|
|
{y.GetSimpleVariable(), 11}};
|
|
|
|
// Test a couple of straightforward explicit cases.
|
|
EXPECT_EQ(dut.EvaluatePartial(eval_point_null).GetMonomials(),
|
|
dut.GetMonomials());
|
|
// TODO(#2216) These fail due to a known drake bug:
|
|
#if 0
|
|
EXPECT_EQ(dut.evaluatePartial(eval_point_x).getMonomials(),
|
|
((23 * y) + 1716).getMonomials());
|
|
EXPECT_EQ(dut.evaluatePartial(eval_point_y).getMonomials(),
|
|
((5 * x * x * x) + (33 * x) + 23).getMonomials());
|
|
#endif
|
|
|
|
// Test that every order of partial and then complete evaluation gives the
|
|
// same answer.
|
|
const double expected_result = dut.EvaluateMultivariate(eval_point_xy);
|
|
EXPECT_EQ(
|
|
dut.EvaluatePartial(eval_point_null).EvaluateMultivariate(eval_point_xy),
|
|
expected_result);
|
|
EXPECT_EQ(
|
|
dut.EvaluatePartial(eval_point_xy).EvaluateMultivariate(eval_point_null),
|
|
expected_result);
|
|
EXPECT_EQ(
|
|
dut.EvaluatePartial(eval_point_x).EvaluateMultivariate(eval_point_y),
|
|
expected_result);
|
|
EXPECT_EQ(
|
|
dut.EvaluatePartial(eval_point_y).EvaluateMultivariate(eval_point_x),
|
|
expected_result);
|
|
|
|
// Test that zeroing out one term gives a sensible result.
|
|
EXPECT_EQ(dut.EvaluatePartial(
|
|
std::map<Polynomiald::VarType, double>{{x.GetSimpleVariable(), 0}}),
|
|
(2 * y) + 1);
|
|
EXPECT_EQ(dut.EvaluatePartial(
|
|
std::map<Polynomiald::VarType, double>{{y.GetSimpleVariable(), 0}}),
|
|
(5 * x * x * x) + 1);
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, FromExpression) {
|
|
using symbolic::Environment;
|
|
using symbolic::Expression;
|
|
using symbolic::Variable;
|
|
|
|
const Variable x{"x"};
|
|
const Variable y{"y"};
|
|
const Variable z{"z"};
|
|
Environment env{{x, 1.0}, {y, 2.0}, {z, 3.0}};
|
|
const map<Polynomiald::VarType, double> eval_point{
|
|
{x.get_id(), env[x]}, {y.get_id(), env[y]}, {z.get_id(), env[z]}};
|
|
|
|
const Expression e0{42.0};
|
|
const Expression e1{pow(x, 2)};
|
|
const Expression e2{3 + x + y + z};
|
|
const Expression e3{1 + pow(x, 2) + pow(y, 2)};
|
|
const Expression e4{pow(x, 2) * pow(y, 2)};
|
|
const Expression e5{pow(x + y + z, 3)};
|
|
const Expression e6{pow(x + y + z, 3) / 10};
|
|
const Expression e7{-pow(y, 3)};
|
|
const Expression e8{pow(pow(x, 3), 1.0 / 3)};
|
|
|
|
EXPECT_NEAR(
|
|
e0.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e0).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
EXPECT_NEAR(
|
|
e1.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e1).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
EXPECT_NEAR(
|
|
e2.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e2).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
EXPECT_NEAR(
|
|
e3.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e3).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
EXPECT_NEAR(
|
|
e4.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e4).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
EXPECT_NEAR(
|
|
e5.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e5).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
EXPECT_NEAR(
|
|
e6.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e6).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
EXPECT_NEAR(
|
|
e7.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e7).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
EXPECT_NEAR(
|
|
e8.Evaluate(env),
|
|
Polynomial<double>::FromExpression(e8).EvaluateMultivariate(eval_point),
|
|
1e-8);
|
|
|
|
const vector<Expression> test_vec{
|
|
log(x),
|
|
abs(x),
|
|
exp(x),
|
|
sqrt(x),
|
|
sin(x),
|
|
cos(x),
|
|
tan(x),
|
|
asin(x),
|
|
acos(x),
|
|
atan(x),
|
|
atan2(x, y),
|
|
sinh(x),
|
|
cosh(x),
|
|
tanh(x),
|
|
min(x, y),
|
|
max(x, y),
|
|
ceil(x),
|
|
floor(x),
|
|
if_then_else(x > y, x, y),
|
|
Expression::NaN(),
|
|
symbolic::uninterpreted_function("uf", {x, y})};
|
|
for (const Expression& e : test_vec) {
|
|
EXPECT_FALSE(e.is_polynomial());
|
|
EXPECT_THROW(Polynomial<double>::FromExpression(e), runtime_error);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void TestScalarType() {
|
|
Eigen::Vector3d coeffs(1., 2., 3.);
|
|
const Polynomial<T> p(coeffs);
|
|
EXPECT_NEAR(ExtractDoubleOrThrow(p.EvaluateUnivariate(0.0)), coeffs(0),
|
|
1e-14);
|
|
|
|
EXPECT_THROW(p.Roots(), std::runtime_error);
|
|
EXPECT_TRUE(static_cast<bool>(p.CoefficientsAlmostEqual(p, 1e-14)));
|
|
|
|
Polynomial<T> x("x", 1);
|
|
Polynomial<T> y("y", 1);
|
|
const std::map<Polynomiald::VarType, double> eval_point = {
|
|
{x.GetSimpleVariable(), 1},
|
|
{y.GetSimpleVariable(), 2}};
|
|
EXPECT_NEAR(
|
|
ExtractDoubleOrThrow((x * x + y).EvaluateMultivariate(eval_point)), 3,
|
|
1e-14);
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, ScalarTypes) {
|
|
TestScalarType<AutoDiffXd>();
|
|
TestScalarType<symbolic::Expression>();
|
|
|
|
// Checks that we can create an instance, Polynomial<T>(0). `Scalar(0)` (where
|
|
// Scalar = Polynomial<T>) is a common pattern in Eigen internals and we want
|
|
// to make sure that we can build these instances.
|
|
const Polynomial<double> p_double(0);
|
|
const Polynomial<AutoDiffXd> p_autodiffxd(0);
|
|
const Polynomial<symbolic::Expression> p_symbolic(0);
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, CoefficientsAlmostEqualTest) {
|
|
Polynomiald x = Polynomiald("x");
|
|
Polynomiald y = Polynomiald("y");
|
|
|
|
EXPECT_FALSE(x.CoefficientsAlmostEqual(y));
|
|
EXPECT_TRUE((x + y).CoefficientsAlmostEqual(y + x));
|
|
|
|
EXPECT_TRUE((x + x * x + 3 * pow(x, 3))
|
|
.CoefficientsAlmostEqual(3 * pow(x, 3) + x + x * x));
|
|
|
|
// Test relative and absolute tolerance.
|
|
EXPECT_TRUE(
|
|
(100 * x).CoefficientsAlmostEqual(99 * x, 0.1, ToleranceType::kRelative));
|
|
EXPECT_FALSE(
|
|
(100 * x).CoefficientsAlmostEqual(99 * x, 0.1, ToleranceType::kAbsolute));
|
|
EXPECT_FALSE((0.01 * x).CoefficientsAlmostEqual(0.02 * x, 0.1,
|
|
ToleranceType::kRelative));
|
|
EXPECT_TRUE((0.01 * x).CoefficientsAlmostEqual(0.02 * x, 0.1,
|
|
ToleranceType::kAbsolute));
|
|
|
|
// Test missing monomials.
|
|
EXPECT_FALSE((x + y + 0.01 * x * x).CoefficientsAlmostEqual(x + y));
|
|
EXPECT_FALSE((x + y).CoefficientsAlmostEqual(x + y + 0.01 * x * x));
|
|
// Missing monomials is ok only for absolute tolerance.
|
|
EXPECT_TRUE(
|
|
(x + y + 0.01 * x * x)
|
|
.CoefficientsAlmostEqual(x + y, 0.02, ToleranceType::kAbsolute));
|
|
EXPECT_TRUE((x + y).CoefficientsAlmostEqual(x + y + 0.01 * x * x, 0.02,
|
|
ToleranceType::kAbsolute));
|
|
EXPECT_FALSE(
|
|
(x + y + 0.01 * x * x)
|
|
.CoefficientsAlmostEqual(x + y, 0.02, ToleranceType::kRelative));
|
|
EXPECT_FALSE((x + y).CoefficientsAlmostEqual(x + y + 0.01 * x * x, 0.02,
|
|
ToleranceType::kRelative));
|
|
}
|
|
|
|
GTEST_TEST(PolynomialTest, SubsitutionTest) {
|
|
Polynomiald x = Polynomiald("x");
|
|
Polynomiald y = Polynomiald("y");
|
|
|
|
Polynomiald::VarType xvar = x.GetSimpleVariable();
|
|
Polynomiald::VarType yvar = y.GetSimpleVariable();
|
|
|
|
EXPECT_TRUE(x.Substitute(xvar, y).CoefficientsAlmostEqual(y));
|
|
|
|
Polynomiald p1 = x + 3 * x * x + 3 * y;
|
|
EXPECT_TRUE(
|
|
p1.Substitute(xvar, 1 + x)
|
|
.CoefficientsAlmostEqual(1 + x + 3 * (1 + x) * (1 + x) + 3 * y));
|
|
EXPECT_TRUE(p1.Substitute(yvar, 1 + x)
|
|
.CoefficientsAlmostEqual(3 + 4 * x + 3 * x * x));
|
|
EXPECT_TRUE(p1.Substitute(xvar, x * x)
|
|
.CoefficientsAlmostEqual(x * x + 3 * pow(x, 4) + 3 * y));
|
|
}
|
|
|
|
} // anonymous namespace
|
|
} // namespace drake
|