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.
Conception/drake-master/math/barycentric.h

182 lines
7.4 KiB

#pragma once
#include <iterator>
#include <memory>
#include <set>
#include <vector>
#include <Eigen/Dense>
#include "drake/common/drake_assert.h"
#include "drake/common/drake_copyable.h"
#include "drake/common/eigen_types.h"
namespace drake {
namespace math {
/// Represents a multi-linear function (from vector inputs to vector outputs) by
/// interpolating between points on a mesh using (triangular) barycentric
/// interpolation.
///
/// For a technical description of barycentric interpolation, see e.g.
/// Remi Munos and Andrew Moore, "Barycentric Interpolators for Continuous
/// Space and Time Reinforcement Learning", NIPS 1998
///
/// @tparam_double_only
template <typename T>
class BarycentricMesh {
// TODO(russt): This is also an instance of a "linear function approximator"
// -- a class of parameterized functions that take the form
// output = parameters*φ(input),
// where
// parameters is a matrix of size num_outputs x num_features, with column i
// the vector value of ith mesh point.
// φ(input) is a vector of length num_features, where element i is the
// interpolation coefficient of the ith mesh point.
// Only num_interpolants of these features are non-zero in any query.
//
// If we implement more function approximators, then I'm tempted to
// call the base classes e.g. ParameterizedFunction and
// LinearlyParameterizedFunction.
//
// Note: here we have a matrix of parameters and a feature vector, when the
// more typical case of linear function approximators would use a vector of
// parameters and a feature matrix.
public:
DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(BarycentricMesh);
/// The mesh is represented by a std::set (to ensure uniqueness and provide
/// logarithmic lookups) of coordinates in each input dimension. Note: The
/// values are type double, not T (We do not plan to take gradients, etc w/
/// respect to them).
typedef std::set<double> Coordinates;
typedef std::vector<Coordinates> MeshGrid;
/// Constructs the mesh.
explicit BarycentricMesh(MeshGrid input_grid);
// Accessor methods.
const MeshGrid& get_input_grid() const { return input_grid_; }
int get_input_size() const { return input_grid_.size(); }
int get_num_mesh_points() const {
int num_mesh_points = 1;
for (const auto& coords : input_grid_) {
num_mesh_points *= coords.size();
}
return num_mesh_points;
}
int get_num_interpolants() const { return num_interpolants_; }
/// Writes the position of a mesh point in the input space referenced by its
/// scalar index to @p point.
/// @param index must be in [0, get_num_mesh_points).
/// @param point is set to the num_inputs-by-1 location of the mesh point.
void get_mesh_point(int index, EigenPtr<Eigen::VectorXd> point) const;
/// Returns the position of a mesh point in the input space referenced by its
/// scalar index to @p point.
/// @param index must be in [0, get_num_mesh_points).
VectorX<T> get_mesh_point(int index) const;
/// Returns a matrix with all of the mesh points, one per column.
MatrixX<T> get_all_mesh_points() const;
/// Writes the mesh indices used for interpolation to @p mesh_indices, and the
/// interpolating coefficients to @p weights. Inputs that are outside the
/// bounding box of the input_grid are interpolated as though they were
/// projected (elementwise) to the closest face of the defined mesh.
///
/// @param input must be a vector of length get_num_inputs().
/// @param mesh_indices is a pointer to a vector of length
/// get_num_interpolants().
/// @param weights is a vector of coefficients (which sum to 1) of length
/// get_num_interpolants().
void EvalBarycentricWeights(const Eigen::Ref<const VectorX<T>>& input,
EigenPtr<Eigen::VectorXi> mesh_indices,
EigenPtr<VectorX<T>> weights) const;
/// Evaluates the function at the @p input values, by interpolating between
/// the values at @p mesh_values. Inputs that are outside the
/// bounding box of the input_grid are interpolated as though they were
/// projected (elementwise) to the closest face of the defined mesh.
///
/// Note that the dimension of the output vector is completely defined by the
/// mesh_values argument. This class does not maintain any information
/// related to the size of the output.
///
/// @param mesh_values is a num_outputs by get_num_mesh_points() matrix
/// containing the points to interpolate between. The order of the columns
/// must be consistent with the mesh indices curated by this class, as exposed
/// by get_mesh_point().
/// @param input must be a vector of length get_num_inputs().
/// @param output is the interpolated vector of length num_outputs
void Eval(const Eigen::Ref<const MatrixX<T>>& mesh_values,
const Eigen::Ref<const VectorX<T>>& input,
EigenPtr<VectorX<T>> output) const;
/// Returns the function evaluated at @p input.
VectorX<T> Eval(const Eigen::Ref<const MatrixX<T>>& mesh_values,
const Eigen::Ref<const VectorX<T>>& input) const;
/// Performs Eval, but with the possibility of the values on the mesh
/// having a different scalar type than the values defining the mesh
/// (symbolic::Expression containing decision variables for an optimization
/// problem is an important example)
/// @tparam ValueT defines the scalar type of the mesh_values and the output.
/// @see Eval
template <typename ValueT = T>
void EvalWithMixedScalars(
const Eigen::Ref<const MatrixX<ValueT>>& mesh_values,
const Eigen::Ref<const VectorX<T>>& input,
EigenPtr<VectorX<ValueT>> output) const {
DRAKE_DEMAND(output != nullptr);
DRAKE_DEMAND(input.size() == get_input_size());
DRAKE_DEMAND(mesh_values.cols() == get_num_mesh_points());
Eigen::VectorXi mesh_indices(num_interpolants_);
VectorX<T> weights(num_interpolants_);
EvalBarycentricWeights(input, &mesh_indices, &weights);
*output = weights[0] * mesh_values.col(mesh_indices[0]);
for (int i = 1; i < num_interpolants_; i++) {
*output += weights[i] * mesh_values.col(mesh_indices[i]);
}
}
/// Returns the function evaluated at @p input.
template <typename ValueT = T>
VectorX<ValueT> EvalWithMixedScalars(
const Eigen::Ref<const MatrixX<ValueT>>& mesh_values,
const Eigen::Ref<const VectorX<T>>& input) const {
VectorX<ValueT> output(mesh_values.rows());
EvalWithMixedScalars<ValueT>(mesh_values, input, &output);
return output;
}
/// Evaluates @p vector_func at all input mesh points and extracts the mesh
/// value matrix that should be used to approximate the function with this
/// barycentric interpolation.
///
/// @code
/// MatrixXd mesh_values = bary.MeshValuesFrom(
/// [](const auto& x) { return Vector1d(std::sin(x[0])); });
/// @endcode
MatrixX<T> MeshValuesFrom(
const std::function<VectorX<T>(const Eigen::Ref<const VectorX<T>>&)>&
vector_func) const;
private:
MeshGrid input_grid_; // Specifies the location of the mesh points in
// the input space.
std::vector<int> stride_; // The number of elements to skip to arrive at the
// next value (per input dimension)
int num_interpolants_{1}; // The number of points used in any interpolation.
};
} // namespace math
} // namespace drake
extern template class ::drake::math::BarycentricMesh<double>;