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.
398 lines
14 KiB
398 lines
14 KiB
#pragma once
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <Eigen/Core>
|
|
|
|
#include "drake/common/drake_assert.h"
|
|
#include "drake/common/drake_copyable.h"
|
|
#include "drake/common/eigen_types.h"
|
|
#include "drake/common/fmt_ostream.h"
|
|
#include "drake/common/polynomial.h"
|
|
#include "drake/common/symbolic/expression.h"
|
|
#include "drake/math/autodiff.h"
|
|
#include "drake/solvers/function.h"
|
|
|
|
namespace drake {
|
|
namespace solvers {
|
|
|
|
/**
|
|
* Provides an abstract interface to represent an expression, mapping a fixed
|
|
* or dynamic number of inputs to a fixed number of outputs, that may be
|
|
* evaluated on a scalar type of double or AutoDiffXd.
|
|
*
|
|
* These objects, and its derivatives, are meant to be bound to a given set
|
|
* of variables using the Binding<> class.
|
|
*/
|
|
class EvaluatorBase {
|
|
public:
|
|
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(EvaluatorBase)
|
|
|
|
virtual ~EvaluatorBase() {}
|
|
|
|
// TODO(bradking): consider using a Ref for `y`. This will require the client
|
|
// to do allocation, but also allows it to choose stack allocation instead.
|
|
/**
|
|
* Evaluates the expression.
|
|
* @param[in] x A `num_vars` x 1 input vector.
|
|
* @param[out] y A `num_outputs` x 1 output vector.
|
|
*/
|
|
void Eval(const Eigen::Ref<const Eigen::VectorXd>& x,
|
|
Eigen::VectorXd* y) const {
|
|
DRAKE_ASSERT(x.rows() == num_vars_ || num_vars_ == Eigen::Dynamic);
|
|
DoEval(x, y);
|
|
DRAKE_ASSERT(y->rows() == num_outputs_);
|
|
}
|
|
|
|
// TODO(eric.cousineau): Move this to DifferentiableConstraint derived class
|
|
// if/when we need to support non-differentiable functions (at least, if
|
|
// DifferentiableConstraint is ever implemented).
|
|
/**
|
|
* Evaluates the expression.
|
|
* @param[in] x A `num_vars` x 1 input vector.
|
|
* @param[out] y A `num_outputs` x 1 output vector.
|
|
*/
|
|
void Eval(const Eigen::Ref<const AutoDiffVecXd>& x, AutoDiffVecXd* y) const {
|
|
DRAKE_ASSERT(x.rows() == num_vars_ || num_vars_ == Eigen::Dynamic);
|
|
DoEval(x, y);
|
|
DRAKE_ASSERT(y->rows() == num_outputs_);
|
|
}
|
|
|
|
/**
|
|
* Evaluates the expression.
|
|
* @param[in] x A `num_vars` x 1 input vector.
|
|
* @param[out] y A `num_outputs` x 1 output vector.
|
|
*/
|
|
void Eval(const Eigen::Ref<const VectorX<symbolic::Variable>>& x,
|
|
VectorX<symbolic::Expression>* y) const {
|
|
DRAKE_ASSERT(x.rows() == num_vars_ || num_vars_ == Eigen::Dynamic);
|
|
DoEval(x, y);
|
|
DRAKE_ASSERT(y->rows() == num_outputs_);
|
|
}
|
|
|
|
/**
|
|
* Set a human-friendly description for the evaluator.
|
|
*/
|
|
void set_description(const std::string& description) {
|
|
description_ = description;
|
|
}
|
|
|
|
/**
|
|
* Getter for a human-friendly description for the evaluator.
|
|
*/
|
|
const std::string& get_description() const { return description_; }
|
|
|
|
/**
|
|
* Formats this evaluator into the given stream using `vars` for the bound
|
|
* decision variable names.
|
|
*
|
|
* The size of `vars` must match the `num_vars()` declared by this evaluator.
|
|
* (If `num_vars()` is `Eigen::Dynamic`, then `vars` may be any size.)
|
|
*/
|
|
std::ostream& Display(std::ostream& os,
|
|
const VectorX<symbolic::Variable>& vars) const;
|
|
|
|
/**
|
|
* Formats this evaluator into the given stream, without displaying the
|
|
* decision variables it is bound to.
|
|
*/
|
|
std::ostream& Display(std::ostream& os) const;
|
|
|
|
/**
|
|
* Getter for the number of variables, namely the number of rows in x, as
|
|
* used in Eval(x, y).
|
|
*/
|
|
int num_vars() const { return num_vars_; }
|
|
|
|
/**
|
|
* Getter for the number of outputs, namely the number of rows in y, as used
|
|
* in Eval(x, y).
|
|
*/
|
|
int num_outputs() const { return num_outputs_; }
|
|
|
|
/**
|
|
* Set the sparsity pattern of the gradient matrix ∂y/∂x (the gradient of
|
|
* y value in Eval, w.r.t x in Eval) . gradient_sparsity_pattern contains
|
|
* *all* the pairs of (row_index, col_index) for which the corresponding
|
|
* entries could have non-zero value in the gradient matrix ∂y/∂x.
|
|
*/
|
|
void SetGradientSparsityPattern(
|
|
const std::vector<std::pair<int, int>>& gradient_sparsity_pattern);
|
|
|
|
/**
|
|
* Returns the vector of (row_index, col_index) that contains all the entries
|
|
* in the gradient of Eval function (∂y/∂x) whose value could be non-zero,
|
|
* namely if ∂yᵢ/∂xⱼ could be non-zero, then the pair (i, j) is in
|
|
* gradient_sparsity_pattern.
|
|
* @retval gradient_sparsity_pattern If nullopt, then we regard all entries of
|
|
* the gradient as potentially non-zero.
|
|
*/
|
|
const std::optional<std::vector<std::pair<int, int>>>&
|
|
gradient_sparsity_pattern() const {
|
|
return gradient_sparsity_pattern_;
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* Constructs a evaluator.
|
|
* @param num_outputs. The number of rows in the output.
|
|
* @param num_vars. The number of rows in the input.
|
|
* If the input dimension is not known, then set `num_vars` to Eigen::Dynamic.
|
|
* @param description A human-friendly description.
|
|
* @see Eval(...)
|
|
*/
|
|
EvaluatorBase(int num_outputs, int num_vars,
|
|
const std::string& description = "")
|
|
: num_vars_(num_vars),
|
|
num_outputs_(num_outputs),
|
|
description_(description) {}
|
|
|
|
/**
|
|
* Implements expression evaluation for scalar type double.
|
|
* @param x Input vector.
|
|
* @param y Output vector.
|
|
* @pre x must be of size `num_vars` x 1.
|
|
* @post y will be of size `num_outputs` x 1.
|
|
*/
|
|
virtual void DoEval(const Eigen::Ref<const Eigen::VectorXd>& x,
|
|
Eigen::VectorXd* y) const = 0;
|
|
|
|
/**
|
|
* Implements expression evaluation for scalar type AutoDiffXd.
|
|
* @param x Input vector.
|
|
* @param y Output vector.
|
|
* @pre x must be of size `num_vars` x 1.
|
|
* @post y will be of size `num_outputs` x 1.
|
|
*/
|
|
virtual void DoEval(const Eigen::Ref<const AutoDiffVecXd>& x,
|
|
AutoDiffVecXd* y) const = 0;
|
|
|
|
/**
|
|
* Implements expression evaluation for scalar type symbolic::Expression.
|
|
* @param[in] x Input vector.
|
|
* @param[out] y Output vector.
|
|
* @pre x must be of size `num_vars` x 1.
|
|
* @post y will be of size `num_outputs` x 1.
|
|
*/
|
|
virtual void DoEval(const Eigen::Ref<const VectorX<symbolic::Variable>>& x,
|
|
VectorX<symbolic::Expression>* y) const = 0;
|
|
|
|
/**
|
|
* NVI implementation of Display. The default implementation will report
|
|
* the NiceTypeName, get_description, and list the bound variables.
|
|
* Subclasses may override to customize the message.
|
|
* @pre vars size is consistent with num_vars".
|
|
*/
|
|
virtual std::ostream& DoDisplay(
|
|
std::ostream& os, const VectorX<symbolic::Variable>& vars) const;
|
|
|
|
// Setter for the number of outputs.
|
|
// This method is only meant to be called, if the sub-class structure permits
|
|
// to change the number of outputs. One example is LinearConstraint in
|
|
// solvers/Constraint.h, which can change the number of outputs, if the
|
|
// matrix in the linear constraint is resized.
|
|
void set_num_outputs(int num_outputs) { num_outputs_ = num_outputs; }
|
|
|
|
private:
|
|
int num_vars_{};
|
|
int num_outputs_{};
|
|
std::string description_;
|
|
// gradient_sparsity_pattern_ records the pair (row_index, col_index) that
|
|
// contains the non-zero entries in the gradient of the Eval
|
|
// function. Note that if the entry (row_index, col_index) *can* be non-zero
|
|
// for certain value of x, then it should be included in
|
|
// gradient_sparsity_patten_. When gradient_sparsity_pattern_.has_value() =
|
|
// false, the gradient matrix is regarded as non-sparse, i.e., every entry of
|
|
// the gradient matrix can be non-zero.
|
|
std::optional<std::vector<std::pair<int, int>>> gradient_sparsity_pattern_;
|
|
};
|
|
|
|
/**
|
|
* Print out the evaluator.
|
|
*/
|
|
std::ostream& operator<<(std::ostream& os, const EvaluatorBase& e);
|
|
|
|
/**
|
|
* Implements an evaluator of the form P(x, y...) where P is a multivariate
|
|
* polynomial in x, y, ...
|
|
*
|
|
* The Polynomial class uses a different variable naming scheme; thus the
|
|
* caller must provide a list of Polynomial::VarType variables that correspond
|
|
* to the members of the Binding<> (the individual scalar elements of the
|
|
* given VariableList).
|
|
*/
|
|
class PolynomialEvaluator : public EvaluatorBase {
|
|
public:
|
|
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(PolynomialEvaluator)
|
|
|
|
/**
|
|
* Constructs a polynomial evaluator given a set of polynomials and the
|
|
* corresponding variables.
|
|
* @param polynomials Polynomial vector, a `num_outputs` x 1 vector.
|
|
* @param poly_vars Polynomial variables, a `num_vars` x 1 vector.
|
|
*/
|
|
PolynomialEvaluator(const VectorXPoly& polynomials,
|
|
const std::vector<Polynomiald::VarType>& poly_vars)
|
|
: EvaluatorBase(polynomials.rows(), poly_vars.size()),
|
|
polynomials_(polynomials),
|
|
poly_vars_(poly_vars) {}
|
|
|
|
const VectorXPoly& polynomials() const { return polynomials_; }
|
|
|
|
const std::vector<Polynomiald::VarType>& poly_vars() const {
|
|
return poly_vars_;
|
|
}
|
|
|
|
private:
|
|
void DoEval(const Eigen::Ref<const Eigen::VectorXd>& x,
|
|
Eigen::VectorXd* y) const override;
|
|
|
|
void DoEval(const Eigen::Ref<const AutoDiffVecXd>& x,
|
|
AutoDiffVecXd* y) const override;
|
|
|
|
void DoEval(const Eigen::Ref<const VectorX<symbolic::Variable>>&,
|
|
VectorX<symbolic::Expression>*) const override {
|
|
throw std::logic_error(
|
|
"PolynomialEvaluator does not support symbolic evaluation.");
|
|
}
|
|
|
|
const VectorXPoly polynomials_;
|
|
const std::vector<Polynomiald::VarType> poly_vars_;
|
|
|
|
// To avoid repeated allocation, reuse a map for the evaluation point.
|
|
// Do not assume that these values will persist across invocations!
|
|
// TODO(eric.cousineau): Consider removing this for thread safety?
|
|
mutable std::map<Polynomiald::VarType, double> double_evaluation_point_temp_;
|
|
mutable std::map<Polynomiald::VarType, AutoDiffXd>
|
|
taylor_evaluation_point_temp_;
|
|
};
|
|
|
|
/**
|
|
* An evaluator that may be specified using a callable object. Consider
|
|
* constructing these instances using MakeFunctionEvaluator(...).
|
|
* @tparam F The function / functor's type.
|
|
*/
|
|
template <typename F>
|
|
class FunctionEvaluator : public EvaluatorBase {
|
|
public:
|
|
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(FunctionEvaluator)
|
|
|
|
/**
|
|
* Constructs an instance by copying from an lvalue or rvalue of `F`.
|
|
* @tparam FF Perfect-forwarding type of `F` (e.g., `const F&`, `F&&`).
|
|
* @param f The callable object. If rvalue, this value will be std::move'd.
|
|
* Otherwise, it will be copied.
|
|
* @param args Arguments to be forwarded to EvaluatorBase constructor.
|
|
*/
|
|
template <typename FF, typename... Args>
|
|
FunctionEvaluator(FF&& f, Args&&... args)
|
|
: EvaluatorBase(internal::FunctionTraits<F>::numOutputs(f),
|
|
internal::FunctionTraits<F>::numInputs(f),
|
|
std::forward<Args>(args)...),
|
|
f_(std::forward<FF>(f)) {}
|
|
|
|
private:
|
|
void DoEval(const Eigen::Ref<const Eigen::VectorXd>& x,
|
|
Eigen::VectorXd* y) const override {
|
|
y->resize(internal::FunctionTraits<F>::numOutputs(f_));
|
|
DRAKE_ASSERT(static_cast<size_t>(x.rows()) ==
|
|
internal::FunctionTraits<F>::numInputs(f_));
|
|
DRAKE_ASSERT(static_cast<size_t>(y->rows()) ==
|
|
internal::FunctionTraits<F>::numOutputs(f_));
|
|
internal::FunctionTraits<F>::eval(f_, x, y);
|
|
}
|
|
|
|
void DoEval(const Eigen::Ref<const AutoDiffVecXd>& x,
|
|
AutoDiffVecXd* y) const override {
|
|
y->resize(internal::FunctionTraits<F>::numOutputs(f_));
|
|
DRAKE_ASSERT(static_cast<size_t>(x.rows()) ==
|
|
internal::FunctionTraits<F>::numInputs(f_));
|
|
DRAKE_ASSERT(static_cast<size_t>(y->rows()) ==
|
|
internal::FunctionTraits<F>::numOutputs(f_));
|
|
internal::FunctionTraits<F>::eval(f_, x, y);
|
|
}
|
|
|
|
void DoEval(const Eigen::Ref<const VectorX<symbolic::Variable>>&,
|
|
VectorX<symbolic::Expression>*) const override {
|
|
throw std::logic_error(
|
|
"FunctionEvaluator does not support symbolic evaluation.");
|
|
}
|
|
|
|
const F f_;
|
|
};
|
|
|
|
/**
|
|
* Creates a FunctionEvaluator instance bound to a given callable object.
|
|
* @tparam FF Perfect-forwarding type of `F` (e.g., `const F&`, `F&&`).
|
|
* @param f Callable function object.
|
|
* @return An implementation of EvaluatorBase using the callable object.
|
|
* @relates FunctionEvaluator
|
|
*/
|
|
template <typename FF>
|
|
std::shared_ptr<EvaluatorBase> MakeFunctionEvaluator(FF&& f) {
|
|
using F = std::decay_t<FF>;
|
|
return std::make_shared<FunctionEvaluator<F>>(std::forward<FF>(f));
|
|
}
|
|
|
|
/**
|
|
* Defines a simple evaluator with no outputs that takes a callback function
|
|
* pointer. This is intended for debugging / visualization of intermediate
|
|
* results during an optimization (for solvers that support it).
|
|
*/
|
|
class VisualizationCallback : public EvaluatorBase {
|
|
public:
|
|
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(VisualizationCallback)
|
|
|
|
typedef std::function<void(const Eigen::Ref<const Eigen::VectorXd>&)>
|
|
CallbackFunction;
|
|
|
|
VisualizationCallback(int num_inputs, const CallbackFunction& callback,
|
|
const std::string& description = "")
|
|
: EvaluatorBase(0, num_inputs, description), callback_(callback) {}
|
|
|
|
void EvalCallback(const Eigen::Ref<const Eigen::VectorXd>& x) const {
|
|
DRAKE_ASSERT(x.size() == num_vars());
|
|
callback_(x);
|
|
}
|
|
|
|
private:
|
|
void DoEval(const Eigen::Ref<const Eigen::VectorXd>& x,
|
|
Eigen::VectorXd* y) const override {
|
|
DRAKE_ASSERT(x.size() == num_vars());
|
|
y->resize(0);
|
|
callback_(x);
|
|
}
|
|
|
|
void DoEval(const Eigen::Ref<const AutoDiffVecXd>& x,
|
|
AutoDiffVecXd* y) const override {
|
|
DRAKE_ASSERT(x.size() == num_vars());
|
|
y->resize(0);
|
|
callback_(math::ExtractValue(x));
|
|
}
|
|
|
|
void DoEval(const Eigen::Ref<const VectorX<symbolic::Variable>>&,
|
|
VectorX<symbolic::Expression>*) const override {
|
|
throw std::logic_error(
|
|
"VisualizationCallback does not support symbolic evaluation.");
|
|
}
|
|
|
|
const CallbackFunction callback_;
|
|
};
|
|
|
|
} // namespace solvers
|
|
} // namespace drake
|
|
|
|
// TODO(jwnimmer-tri) Add a real formatter and deprecate the operator<<.
|
|
namespace fmt {
|
|
template <typename T>
|
|
struct formatter<
|
|
T,
|
|
std::enable_if_t<std::is_base_of_v<drake::solvers::EvaluatorBase, T>, char>>
|
|
: drake::ostream_formatter {};
|
|
} // namespace fmt
|