#include "drake/common/symbolic/monomial_util.h" #include "drake/solvers/mathematical_program.h" #include "drake/tools/performance/fixture_common.h" namespace drake { namespace solvers { namespace { static void BenchmarkSosProgram1(benchmark::State& state) { // NOLINT // Formulate a mathematical program with sum-of-squares constraints. This // will involve a lot of computation on symbolic::Polynomial and imposing // linear constraints. // We construct symbolic::Polynomial directly from a Monomial map instead of // parsing a symbolic::Expression. for (auto _ : state) { MathematicalProgram prog; // Impose a constraint p_i(x).dot(q_i(x)) is sos, where both p_i(x) and // q_i(x) are vectors of polynomials. const auto x = prog.NewIndeterminates<7>(); const symbolic::Variables x_vars(x); const auto monomial_basis = symbolic::MonomialBasis(x_vars, 3); for (int i = 0; i < 10; ++i) { VectorX p_i(15); VectorX q_i(p_i.rows()); for (int j = 0; j < p_i.rows(); ++j) { symbolic::Polynomial::MapType p_map; for (int k = 0; k < monomial_basis.rows(); ++k) { p_map.emplace(monomial_basis(k), static_cast(i * j) / 100.0 + k / 10.0 + 1); } p_i(j) = symbolic::Polynomial(std::move(p_map)); q_i(j) = prog.NewFreePolynomial(x_vars, 2); } prog.AddSosConstraint(p_i.dot(q_i)); } } } static void BenchmarkSosProgram2(benchmark::State& state) { // NOLINT // Formulate a mathematical program with sum-of-squares constraints. This // will involve a lot of computation on symbolic::Polynomial and imposing // linear constraints. Specifically it would involve the multiplication // between two polynomials, one of the polynomial has all the coefficients as // double constants, and the other polynomial coefficients are linear // expressions, constructed using symbolic polynomial operations. This is // different from BenchmarkSosProgram1 where the second polynomial // coefficients are symbolic variables. We construct symbolic::Polynomial // directly from a Monomial map instead of parsing a symbolic::Expression. for (auto _ : state) { MathematicalProgram prog; // Impose a constraint p(x).dot(q(x)) is sos, where both p(x) and // q(x) are both polynomials. // where q(x) = ∂V/∂x * f(x) // This ∂V/∂x * f(x) is often found in Lyapunov stability analysis. const auto x = prog.NewIndeterminates<5>(); const symbolic::Variables x_vars(x); const VectorX monomial_basis = symbolic::MonomialBasis(x_vars, 3); const symbolic::Polynomial V = prog.NewFreePolynomial(x_vars, 4); const Eigen::Matrix dVdx = V.Jacobian(x); symbolic::Polynomial::MapType p_map; for (int k = 0; k < monomial_basis.rows(); ++k) { p_map.emplace(monomial_basis(k), static_cast(k) / 10.0 + 1); } const symbolic::Polynomial p = symbolic::Polynomial(std::move(p_map)); // Construct f(x) ∈ ℝⁿ[x] where n = x.rows() VectorX f(x.rows()); for (int i = 0; i < x.rows(); ++i) { symbolic::Polynomial::MapType f_map; for (int k = 0; k < monomial_basis.rows(); ++k) { f_map.emplace(monomial_basis(k), static_cast(k) * 2.0 + 1); } f(i) = symbolic::Polynomial(std::move(f_map)); } const symbolic::Polynomial q = dVdx.dot(f); prog.AddSosConstraint(p * q); } } /** * This program is reported in github issue * https://github.com/RobotLocomotion/drake/issues/17160 * This program tries to find a lower bound for the cost-to-go that satisfies * HJB inequality. Note that we create the right-hand side polynomial of the HJB * inequality but don't impose any non-negative constraint on this polynomial. * The reason is that this polynomial's coefficient isn't a linear expression of * our decision variables. This benchmark program tests parsing a symbolic * expression to a polynomial, but not imposing constraint on the polynomial. */ static void BenchmarkSosProgram3(benchmark::State& state) { // NOLINT for (auto _ : state) { MathematicalProgram prog; const int nz = 3; const int degree = 8; const auto z = prog.NewIndeterminates(nz, "z"); const symbolic::Polynomial J = prog.NewFreePolynomial(symbolic::Variables(z), degree); const symbolic::Expression J_expr = J.ToExpression(); const RowVectorX dJdz = J_expr.Jacobian(z); const Eigen::Vector3d f2(0, 0, 1); const symbolic::Expression u_opt = -0.5 * dJdz.dot(f2); const Eigen::Vector3d z0(0, 1, 0); using std::pow; const symbolic::Expression one_step_cost = (z - z0).dot(z - z0) + pow(u_opt, 2); const Vector3 f(z(1) + z(2), -z(0) + z(2), (z(0) + u_opt - z(2))); const symbolic::Expression rhs = one_step_cost + dJdz.dot(f); const symbolic::Polynomial rhs_poly(rhs, symbolic::Variables(z)); } } BENCHMARK(BenchmarkSosProgram1); BENCHMARK(BenchmarkSosProgram2); BENCHMARK(BenchmarkSosProgram3); } // namespace } // namespace solvers } // namespace drake