Skip to content

Commit

Permalink
Create CostShim, move ConstraintImpl to FunctionCost, add supporting …
Browse files Browse the repository at this point in the history
…test. PR 1 for RobotLocomotion#5890.
  • Loading branch information
EricCousineau-TRI committed Apr 25, 2017
1 parent 44ede42 commit 89123d6
Show file tree
Hide file tree
Showing 11 changed files with 531 additions and 105 deletions.
56 changes: 53 additions & 3 deletions drake/solvers/BUILD
Expand Up @@ -32,6 +32,33 @@ drake_cc_library(
],
)

# TODO(eric.cousineau): Remove CostShim that produces coupling
drake_cc_library(
name = "cost",
srcs = ["cost.cc"],
hdrs = ["cost.h"],
deps = [
":constraint",
":function",
],
)

drake_cc_library(
name = "create_cost",
srcs = [],
hdrs = ["create_cost.h"],
deps = [
":binding",
":cost",
"//drake/common",
"//drake/common:autodiff",
"//drake/common:monomial",
"//drake/common:number_traits",
"//drake/common:polynomial",
"//drake/common:symbolic",
],
)

drake_cc_library(
name = "decision_variable",
srcs = ["decision_variable.cc"],
Expand Down Expand Up @@ -88,9 +115,8 @@ drake_cc_library(
],
deps = [
":binding",
":constraint",
":create_cost",
":decision_variable",
":function",
":gurobi_solver",
":ipopt_solver",
":mosek_solver",
Expand Down Expand Up @@ -157,6 +183,17 @@ drake_cc_library(
],
)

drake_cc_library(
name = "generic_trivial_cost",
testonly = 1,
srcs = [],
hdrs = ["test/generic_trivial_costs.h"],
deps = [
":constraint",
":cost",
],
)

drake_cc_library(
name = "mathematical_program_test_util",
testonly = 1,
Expand Down Expand Up @@ -217,7 +254,7 @@ drake_cc_library(
],
deps = [
":binding",
":constraint",
":create_cost",
":decision_variable",
":function",
"//drake/common",
Expand Down Expand Up @@ -346,6 +383,18 @@ drake_cc_googletest(
],
)

drake_cc_googletest(
name = "cost_test",
deps = [
":cost",
":create_cost",
":generic_trivial_cost",
"//drake/common:eigen_matrix_compare",
"//drake/common:is_dynamic_castable",
"//drake/math:gradient",
],
)

# Temporarily disabled because we have no Gurobi license in CI. #5862
# TODO(hongkai-dai): Separate this test into a test that uses Gurobi, and a
# test that doesn't.
Expand Down Expand Up @@ -454,6 +503,7 @@ drake_cc_googletest(
"test/mathematical_program_test.cc",
],
deps = [
":generic_trivial_cost",
":mathematical_program",
":mathematical_program_test_util",
"//drake/common:eigen_matrix_compare",
Expand Down
3 changes: 3 additions & 0 deletions drake/solvers/CMakeLists.txt
Expand Up @@ -29,6 +29,7 @@ set(optimization_requires)
list(APPEND optimization_files
${solver_headers}
constraint.cc
cost.cc
decision_variable.cc
equality_constrained_qp_solver.cc
linear_system_solver.cc
Expand Down Expand Up @@ -77,6 +78,8 @@ drake_install_headers(
${solver_headers}
binding.h
constraint.h
cost.h
create_cost.h
function.h
mathematical_program.h
mathematical_program_solver_interface.h
Expand Down
18 changes: 18 additions & 0 deletions drake/solvers/cost.cc
@@ -0,0 +1,18 @@
#include "drake/solvers/cost.h"

namespace drake {
namespace solvers {

void Cost::DoEval(const Eigen::Ref<const Eigen::VectorXd>& x,
// TODO(#2274) Fix NOLINTNEXTLINE(runtime/references).
Eigen::VectorXd& y) const {
impl_->Eval(x, y);
}
void Cost::DoEval(const Eigen::Ref<const AutoDiffVecXd>& x,
// TODO(#2274) Fix NOLINTNEXTLINE(runtime/references).
AutoDiffVecXd& y) const {
impl_->Eval(x, y);
}

} // namespace solvers
} // namespace drake
145 changes: 145 additions & 0 deletions drake/solvers/cost.h
@@ -0,0 +1,145 @@
#pragma once

#include <memory>
#include <utility>

#include "drake/solvers/constraint.h"
#include "drake/solvers/function.h"

namespace drake {
namespace solvers {

// TODO(eric.cousineau): Remove stopgap, and actually have Constraint and
// Cost be different classes. Consider using some common evaluation base.

/**
* Stopgap definition that permits Cost to use functionality in Constraint.
* Using an internal implementation permits child costs to inherit directly
* from cost, thus be convertible to a cost.
*/
class Cost : public Constraint {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(Cost)

explicit Cost(const std::shared_ptr<Constraint>& impl)
: Constraint(impl->num_constraints(), impl->num_vars()), impl_(impl) {
// Costs may only be scalar.
DRAKE_DEMAND(impl->num_constraints() == 1);
}

protected:
void DoEval(const Eigen::Ref<const Eigen::VectorXd>& x,
// TODO(#2274) Fix NOLINTNEXTLINE(runtime/references).
Eigen::VectorXd& y) const override;

void DoEval(const Eigen::Ref<const AutoDiffVecXd>& x,
// TODO(#2274) Fix NOLINTNEXTLINE(runtime/references).
AutoDiffVecXd& y) const override;

private:
std::shared_ptr<Constraint> impl_;
};

/**
* Stopgap class to provide functionality as constraint, but allow templates to
* detect a difference from results from CreateConstraint and CreateCost.
* @tparam C Constraint type to inherit from.
*/
template <typename C>
class CostShim : public Cost {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(CostShim)

template <typename... Args>
explicit CostShim(Args&&... args)
: Cost(std::make_shared<C>(std::forward<Args>(args)...)) {}
};

class LinearCost : public CostShim<LinearConstraint> {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(LinearCost)

// Inherit forwarding constructor.
using CostShim::CostShim;
};

class QuadraticCost : public CostShim<QuadraticConstraint> {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(QuadraticCost)

// Inherit forwarding constructor.
using CostShim::CostShim;
};

class PolynomialCost : public CostShim<PolynomialConstraint> {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(PolynomialCost)

// Inherit forwarding constructor.
using CostShim::CostShim;
};

/**
* A constraint that may be specified using a callable object
* @tparam F The function / functor's type
* @note This is presently in Cost as it is the only place used. Once Cost is
* its own proper class, this name will be transitioned to FunctionCost.
*/
template <typename F>
class FunctionConstraint : public Constraint {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(FunctionConstraint)

// Construct by copying from an lvalue.
template <typename... Args>
FunctionConstraint(const F& f, Args&&... args)
: Constraint(detail::FunctionTraits<F>::numOutputs(f),
detail::FunctionTraits<F>::numInputs(f),
std::forward<Args>(args)...),
f_(f) {}

// Construct by moving from an rvalue.
template <typename... Args>
FunctionConstraint(F&& f, Args&&... args)
: Constraint(detail::FunctionTraits<F>::numOutputs(f),
detail::FunctionTraits<F>::numInputs(f),
std::forward<Args>(args)...),
f_(std::forward<F>(f)) {}

protected:
void DoEval(const Eigen::Ref<const Eigen::VectorXd>& x,
Eigen::VectorXd& y) const override {
y.resize(detail::FunctionTraits<F>::numOutputs(f_));
DRAKE_ASSERT(static_cast<size_t>(x.rows()) ==
detail::FunctionTraits<F>::numInputs(f_));
DRAKE_ASSERT(static_cast<size_t>(y.rows()) ==
detail::FunctionTraits<F>::numOutputs(f_));
detail::FunctionTraits<F>::eval(f_, x, y);
}
void DoEval(const Eigen::Ref<const AutoDiffVecXd>& x,
AutoDiffVecXd& y) const override {
y.resize(detail::FunctionTraits<F>::numOutputs(f_));
DRAKE_ASSERT(static_cast<size_t>(x.rows()) ==
detail::FunctionTraits<F>::numInputs(f_));
DRAKE_ASSERT(static_cast<size_t>(y.rows()) ==
detail::FunctionTraits<F>::numOutputs(f_));
detail::FunctionTraits<F>::eval(f_, x, y);
}

private:
const F f_;
};

template <typename F>
class FunctionCost : public CostShim<FunctionConstraint<F>> {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(FunctionCost)

// Inherit forwarding constructor.
// Must explicitly qualify type due to class-level template.
using Base = CostShim<FunctionConstraint<F>>;
using Base::Base;
};

}; // namespace solvers
} // namespace drake
90 changes: 90 additions & 0 deletions drake/solvers/create_cost.h
@@ -0,0 +1,90 @@
#pragma once

#include <memory>
#include <type_traits>
#include <utility>

#include "drake/common/symbolic_expression.h"
#include "drake/solvers/binding.h"
#include "drake/solvers/constraint.h"
#include "drake/solvers/cost.h"
#include "drake/solvers/function.h"

namespace drake {
namespace solvers {

/** \addtogroup FunctionCostCreators */
/*@{*/

/**
* Converts an input of type @p F to a FunctionCost object.
* @tparam F This class should have functions numInputs(), numOutputs and
* eval(x, y).
* @see detail::FunctionTraits
*/
template <typename F>
std::shared_ptr<Cost> CreateFunctionCost(F&& f) {
return std::make_shared<
FunctionCost<typename std::remove_reference<F>::type>>(
std::forward<F>(f));
}

namespace detail {

// From: drake-distro (git sha: 24452c1)
// //drake/solvers/mathematical_program.h:739
// libstdc++ 4.9 evaluates
// `std::is_convertible<std::unique_ptr<Unrelated>,
// std::shared_ptr<Constraint>>::value`
// incorrectly as `true` so our enable_if overload is not used.
// Provide an explicit alternative for this case.
template <typename A, typename B>
struct is_convertible_workaround : std::is_convertible<A, B> {};
template <typename A, typename B>
struct is_convertible_workaround<std::unique_ptr<A>, std::shared_ptr<B>>
: std::is_convertible<A*, B*> {};

/**
* Template condition to check if @p F is a candidate to be used to construct a
* FunctionCost object for generic costs.
* @note Constraint is used to ensure that we do not preclude cost objects
* that lost their CostShim type somewhere in the process.
*/
template <typename F>
struct is_cost_functor_candidate
: std::integral_constant<
bool,
(!is_convertible_workaround<F, Constraint>::value) &&
(!is_convertible_workaround<
F, std::shared_ptr<Constraint>>::value) &&
(!is_convertible_workaround<
F, std::unique_ptr<Constraint>>::value) &&
(!is_convertible_workaround<F, Binding<Constraint>>::value) &&
(!is_convertible_workaround<F, symbolic::Expression>::value)> {};

} // namespace detail

// TODO(eric.cousineau): For is_cost_functor_candiate, consider
// changing implementation to simply check if F is callable (after removing
// pointers, decaying, etc.)
// @ref http://stackoverflow.com/a/5117641/7829525

/**
* Add costs to the optimization program on decision variables as dictated
* by the Binding constructor.
*
* @tparam F it should define functions numInputs, numOutputs and eval.
*
* @see detail::FunctionTraits for more detail.
*/
template <typename F>
typename std::enable_if<detail::is_cost_functor_candidate<F>::value,
std::shared_ptr<Cost>>::type
CreateCost(F&& f) {
return CreateFunctionCost(std::forward<F>(f));
}

/*@}*/ // \addtogroup FunctionCostCreators

} // namespace solvers
} // namespace drake

0 comments on commit 89123d6

Please sign in to comment.