2014-09-30 18:30:15 +08:00
|
|
|
/* ----------------------------------------------------------------------------
|
|
|
|
|
2015-10-20 06:02:12 +08:00
|
|
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
2014-09-30 18:30:15 +08:00
|
|
|
* Atlanta, Georgia 30332-0415
|
|
|
|
* All Rights Reserved
|
|
|
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
|
|
|
|
|
|
|
* See LICENSE for the license information
|
|
|
|
|
|
|
|
* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file Expression.h
|
|
|
|
* @date September 18, 2014
|
|
|
|
* @author Frank Dellaert
|
|
|
|
* @author Paul Furgale
|
|
|
|
* @brief Expressions for Block Automatic Differentiation
|
|
|
|
*/
|
|
|
|
|
2014-11-22 21:55:32 +08:00
|
|
|
#pragma once
|
|
|
|
|
2020-07-27 17:57:31 +08:00
|
|
|
#include <array>
|
2020-07-28 06:20:16 +08:00
|
|
|
#include <gtsam/config.h>
|
2020-07-27 17:57:31 +08:00
|
|
|
#include <gtsam/base/Testable.h>
|
2014-11-27 00:26:04 +08:00
|
|
|
#include <gtsam/nonlinear/Expression.h>
|
2014-09-30 18:30:15 +08:00
|
|
|
#include <gtsam/nonlinear/NonlinearFactor.h>
|
2023-02-12 04:37:03 +08:00
|
|
|
|
2014-10-15 06:28:53 +08:00
|
|
|
#include <numeric>
|
2023-02-12 04:37:03 +08:00
|
|
|
#include <utility>
|
2015-10-20 06:02:12 +08:00
|
|
|
|
2014-09-30 18:30:15 +08:00
|
|
|
namespace gtsam {
|
|
|
|
|
|
|
|
/**
|
2020-07-26 14:22:40 +08:00
|
|
|
* Factor that supports arbitrary expressions via AD.
|
|
|
|
*
|
|
|
|
* Arbitrary instances of this template can be directly inserted into a factor
|
|
|
|
* graph for optimization. However, to enable the correct (de)serialization of
|
|
|
|
* such instances, the user should declare derived classes from this template,
|
|
|
|
* implementing expresion(), serialize(), clone(), print(), and defining the
|
|
|
|
* corresponding `struct traits<NewFactor> : public Testable<NewFactor> {}`.
|
2020-07-27 14:54:14 +08:00
|
|
|
*
|
|
|
|
* \tparam T Type for measurements.
|
|
|
|
*
|
2014-09-30 18:30:15 +08:00
|
|
|
*/
|
2023-01-07 08:29:15 +08:00
|
|
|
template <typename T>
|
|
|
|
class ExpressionFactor : public NoiseModelFactor {
|
2023-02-06 16:19:15 +08:00
|
|
|
GTSAM_CONCEPT_ASSERT(IsTestable<T>);
|
2014-09-30 18:30:15 +08:00
|
|
|
|
2015-07-09 10:14:23 +08:00
|
|
|
protected:
|
2015-05-14 13:26:24 +08:00
|
|
|
|
|
|
|
typedef ExpressionFactor<T> This;
|
2014-12-26 23:47:51 +08:00
|
|
|
static const int Dim = traits<T>::dimension;
|
2014-10-21 07:26:17 +08:00
|
|
|
|
2015-07-13 03:05:52 +08:00
|
|
|
T measured_; ///< the measurement to be compared with the expression
|
2015-07-12 04:51:30 +08:00
|
|
|
Expression<T> expression_; ///< the expression that is AD enabled
|
|
|
|
FastVector<int> dims_; ///< dimensions of the Jacobian matrices
|
|
|
|
|
2015-05-14 13:26:24 +08:00
|
|
|
|
2016-02-25 03:01:19 +08:00
|
|
|
public:
|
2023-01-12 03:14:26 +08:00
|
|
|
|
2023-01-11 05:07:58 +08:00
|
|
|
// Provide access to the Matrix& version of unwhitenedError:
|
2023-01-07 08:29:15 +08:00
|
|
|
using NoiseModelFactor::unwhitenedError;
|
2023-01-18 06:05:12 +08:00
|
|
|
typedef std::shared_ptr<ExpressionFactor<T> > shared_ptr;
|
2014-09-30 18:30:15 +08:00
|
|
|
|
2017-12-03 09:20:27 +08:00
|
|
|
/**
|
|
|
|
* Constructor: creates a factor from a measurement and measurement function
|
|
|
|
* @param noiseModel the noise model associated with a measurement
|
|
|
|
* @param measurement actual value of the measurement, of type T
|
|
|
|
* @param expression predicts the measurement from Values
|
|
|
|
* The keys associated with the factor, returned by keys(), are sorted.
|
|
|
|
*/
|
2015-07-12 06:55:01 +08:00
|
|
|
ExpressionFactor(const SharedNoiseModel& noiseModel, //
|
|
|
|
const T& measurement, const Expression<T>& expression)
|
2015-07-13 03:05:52 +08:00
|
|
|
: NoiseModelFactor(noiseModel), measured_(measurement) {
|
2015-07-12 06:55:01 +08:00
|
|
|
initialize(expression);
|
2014-09-30 18:30:15 +08:00
|
|
|
}
|
|
|
|
|
2015-07-12 07:11:13 +08:00
|
|
|
/// Destructor
|
2021-01-29 12:02:13 +08:00
|
|
|
~ExpressionFactor() override {}
|
2015-07-12 07:11:13 +08:00
|
|
|
|
2015-07-13 12:43:46 +08:00
|
|
|
/** return the measurement */
|
|
|
|
const T& measured() const { return measured_; }
|
2015-07-13 03:05:52 +08:00
|
|
|
|
2015-07-12 04:51:30 +08:00
|
|
|
/// print relies on Testable traits being defined for T
|
2016-02-25 03:01:19 +08:00
|
|
|
void print(const std::string& s = "",
|
2020-07-26 15:57:54 +08:00
|
|
|
const KeyFormatter& keyFormatter = DefaultKeyFormatter) const override {
|
2015-07-12 04:51:30 +08:00
|
|
|
NoiseModelFactor::print(s, keyFormatter);
|
2016-02-25 03:01:19 +08:00
|
|
|
traits<T>::Print(measured_, "ExpressionFactor with measurement: ");
|
2015-07-12 04:51:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// equals relies on Testable traits being defined for T
|
2020-07-26 15:57:54 +08:00
|
|
|
bool equals(const NonlinearFactor& f, double tol) const override {
|
2015-07-12 04:51:30 +08:00
|
|
|
const ExpressionFactor* p = dynamic_cast<const ExpressionFactor*>(&f);
|
|
|
|
return p && NoiseModelFactor::equals(f, tol) &&
|
2015-07-13 03:05:52 +08:00
|
|
|
traits<T>::Equals(measured_, p->measured_, tol) &&
|
2015-07-12 06:55:01 +08:00
|
|
|
dims_ == p->dims_;
|
2015-07-12 04:51:30 +08:00
|
|
|
}
|
|
|
|
|
2014-10-02 17:01:39 +08:00
|
|
|
/**
|
2015-10-20 06:02:12 +08:00
|
|
|
* Error function *without* the NoiseModel, \f$ z-h(x) -> Local(h(x),z) \f$.
|
2014-10-02 17:01:39 +08:00
|
|
|
* We override this method to provide
|
|
|
|
* both the function evaluation and its derivative(s) in H.
|
|
|
|
*/
|
2020-07-26 15:57:54 +08:00
|
|
|
Vector unwhitenedError(const Values& x,
|
2023-01-12 03:08:00 +08:00
|
|
|
OptionalMatrixVecType H = nullptr) const override {
|
2015-05-12 14:00:50 +08:00
|
|
|
if (H) {
|
|
|
|
const T value = expression_.valueAndDerivatives(x, keys_, dims_, *H);
|
2015-10-20 06:02:12 +08:00
|
|
|
// NOTE(hayk): Doing the reverse, AKA Local(measured_, value) is not correct here
|
|
|
|
// because it would use the tangent space of the measurement instead of the value.
|
|
|
|
return -traits<T>::Local(value, measured_);
|
2014-10-02 17:01:39 +08:00
|
|
|
} else {
|
2014-11-25 18:29:50 +08:00
|
|
|
const T value = expression_.value(x);
|
2015-10-20 06:02:12 +08:00
|
|
|
return -traits<T>::Local(value, measured_);
|
2014-10-02 17:01:39 +08:00
|
|
|
}
|
2014-09-30 18:30:15 +08:00
|
|
|
}
|
|
|
|
|
2023-01-18 06:05:12 +08:00
|
|
|
std::shared_ptr<GaussianFactor> linearize(const Values& x) const override {
|
2014-11-02 20:53:22 +08:00
|
|
|
// Only linearize if the factor is active
|
2014-11-02 21:37:52 +08:00
|
|
|
if (!active(x))
|
2023-01-18 06:05:12 +08:00
|
|
|
return std::shared_ptr<JacobianFactor>();
|
2014-11-02 20:53:22 +08:00
|
|
|
|
2014-11-02 20:45:54 +08:00
|
|
|
// In case noise model is constrained, we need to provide a noise model
|
2015-07-09 10:14:23 +08:00
|
|
|
SharedDiagonal noiseModel;
|
2015-10-20 06:02:12 +08:00
|
|
|
if (noiseModel_ && noiseModel_->isConstrained()) {
|
2023-01-18 06:39:55 +08:00
|
|
|
noiseModel = std::static_pointer_cast<noiseModel::Constrained>(
|
2015-07-09 10:14:23 +08:00
|
|
|
noiseModel_)->unit();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a writeable JacobianFactor in advance
|
2023-01-18 06:05:12 +08:00
|
|
|
std::shared_ptr<JacobianFactor> factor(
|
2015-07-09 10:14:23 +08:00
|
|
|
new JacobianFactor(keys_, dims_, Dim, noiseModel));
|
2014-10-12 17:05:43 +08:00
|
|
|
|
2014-11-01 18:35:49 +08:00
|
|
|
// Wrap keys and VerticalBlockMatrix into structure passed to expression_
|
2014-11-02 19:01:52 +08:00
|
|
|
VerticalBlockMatrix& Ab = factor->matrixObject();
|
2015-05-12 13:21:52 +08:00
|
|
|
internal::JacobianMap jacobianMap(keys_, Ab);
|
2014-11-02 19:01:52 +08:00
|
|
|
|
|
|
|
// Zero out Jacobian so we can simply add to it
|
|
|
|
Ab.matrix().setZero();
|
2014-10-19 17:19:09 +08:00
|
|
|
|
2014-11-25 18:29:50 +08:00
|
|
|
// Get value and Jacobians, writing directly into JacobianFactor
|
2015-05-12 14:00:50 +08:00
|
|
|
T value = expression_.valueAndJacobianMap(x, jacobianMap); // <<< Reverse AD happens here !
|
2014-11-25 18:29:50 +08:00
|
|
|
|
|
|
|
// Evaluate error and set RHS vector b
|
2015-10-20 06:02:12 +08:00
|
|
|
Ab(size()).col(0) = traits<T>::Local(value, measured_);
|
2014-10-14 17:13:49 +08:00
|
|
|
|
2014-11-02 21:42:59 +08:00
|
|
|
// Whiten the corresponding system, Ab already contains RHS
|
2015-10-20 06:02:12 +08:00
|
|
|
if (noiseModel_) {
|
|
|
|
Vector b = Ab(size()).col(0); // need b to be valid for Robust noise models
|
|
|
|
noiseModel_->WhitenSystem(Ab.matrix(), b);
|
|
|
|
}
|
2014-10-11 23:05:53 +08:00
|
|
|
|
2023-02-12 04:37:03 +08:00
|
|
|
return std::move(factor);
|
2014-10-11 23:05:53 +08:00
|
|
|
}
|
2015-05-14 13:26:24 +08:00
|
|
|
|
|
|
|
/// @return a deep copy of this factor
|
2020-07-26 15:57:54 +08:00
|
|
|
gtsam::NonlinearFactor::shared_ptr clone() const override {
|
2023-01-18 06:39:55 +08:00
|
|
|
return std::static_pointer_cast<gtsam::NonlinearFactor>(
|
2015-07-09 10:14:23 +08:00
|
|
|
gtsam::NonlinearFactor::shared_ptr(new This(*this)));
|
|
|
|
}
|
2015-07-12 04:51:30 +08:00
|
|
|
|
2015-07-12 06:55:01 +08:00
|
|
|
protected:
|
|
|
|
ExpressionFactor() {}
|
2015-07-14 14:43:57 +08:00
|
|
|
/// Default constructor, for serialization
|
2015-07-12 06:55:01 +08:00
|
|
|
|
2015-07-14 14:43:57 +08:00
|
|
|
/// Constructor for serializable derived classes
|
2015-07-12 06:55:01 +08:00
|
|
|
ExpressionFactor(const SharedNoiseModel& noiseModel, const T& measurement)
|
2015-07-13 03:05:52 +08:00
|
|
|
: NoiseModelFactor(noiseModel), measured_(measurement) {
|
2015-07-12 06:55:01 +08:00
|
|
|
// Not properly initialized yet, need to call initialize
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Initialize with constructor arguments
|
|
|
|
void initialize(const Expression<T>& expression) {
|
|
|
|
if (!noiseModel_)
|
|
|
|
throw std::invalid_argument("ExpressionFactor: no NoiseModel.");
|
|
|
|
if (noiseModel_->dim() != Dim)
|
|
|
|
throw std::invalid_argument(
|
|
|
|
"ExpressionFactor was created with a NoiseModel of incorrect dimension.");
|
|
|
|
expression_ = expression;
|
|
|
|
|
|
|
|
// Get keys and dimensions for Jacobian matrices
|
|
|
|
// An Expression is assumed unmutable, so we do this now
|
2017-12-03 09:20:27 +08:00
|
|
|
if (keys_.empty()) {
|
|
|
|
// This is the case when called in ExpressionFactor Constructor.
|
|
|
|
// We then take the keys from the expression in sorted order.
|
2023-01-19 08:32:29 +08:00
|
|
|
std::tie(keys_, dims_) = expression_.keysAndDims();
|
2017-12-03 09:20:27 +08:00
|
|
|
} else {
|
|
|
|
// This happens with classes derived from BinaryExpressionFactor etc.
|
|
|
|
// In that case, the keys_ are already defined and we just need to grab
|
|
|
|
// the dimensions in the correct order.
|
|
|
|
std::map<Key, int> keyedDims;
|
|
|
|
expression_.dims(keyedDims);
|
|
|
|
for (Key key : keys_) dims_.push_back(keyedDims[key]);
|
2016-03-08 04:20:42 +08:00
|
|
|
}
|
2015-07-12 06:55:01 +08:00
|
|
|
}
|
|
|
|
|
2015-07-14 14:43:57 +08:00
|
|
|
/// Recreate expression from keys_ and measured_, used in load below.
|
|
|
|
/// Needed to deserialize a derived factor
|
|
|
|
virtual Expression<T> expression() const {
|
|
|
|
throw std::runtime_error("ExpressionFactor::expression not provided: cannot deserialize.");
|
|
|
|
}
|
|
|
|
|
2015-07-12 06:55:01 +08:00
|
|
|
private:
|
2023-01-19 03:36:21 +08:00
|
|
|
#ifdef GTSAM_ENABLE_BOOST_SERIALIZATION
|
2015-07-14 14:43:57 +08:00
|
|
|
/// Save to an archive: just saves the base class
|
|
|
|
template <class Archive>
|
|
|
|
void save(Archive& ar, const unsigned int /*version*/) const {
|
|
|
|
ar << BOOST_SERIALIZATION_BASE_OBJECT_NVP(NoiseModelFactor);
|
|
|
|
ar << boost::serialization::make_nvp("measured_", this->measured_);
|
2015-07-12 06:55:01 +08:00
|
|
|
}
|
|
|
|
|
2015-07-14 14:43:57 +08:00
|
|
|
/// Load from an archive, creating a valid expression using the overloaded
|
|
|
|
/// [expression] method
|
|
|
|
template <class Archive>
|
|
|
|
void load(Archive& ar, const unsigned int /*version*/) {
|
|
|
|
ar >> BOOST_SERIALIZATION_BASE_OBJECT_NVP(NoiseModelFactor);
|
|
|
|
ar >> boost::serialization::make_nvp("measured_", this->measured_);
|
|
|
|
this->initialize(expression());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Indicate that we implement save/load separately, and be friendly to boost
|
|
|
|
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
|
|
|
|
2015-07-12 06:55:01 +08:00
|
|
|
friend class boost::serialization::access;
|
2023-01-19 03:36:21 +08:00
|
|
|
#endif
|
2018-11-06 13:29:38 +08:00
|
|
|
|
|
|
|
// Alignment, see https://eigen.tuxfamily.org/dox/group__TopicStructHavingEigenMembers.html
|
|
|
|
enum { NeedsToAlign = (sizeof(T) % 16) == 0 };
|
|
|
|
public:
|
2020-06-25 05:15:00 +08:00
|
|
|
GTSAM_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign)
|
2015-07-12 06:55:01 +08:00
|
|
|
};
|
|
|
|
// ExpressionFactor
|
|
|
|
|
2015-07-12 04:51:30 +08:00
|
|
|
/// traits
|
|
|
|
template <typename T>
|
|
|
|
struct traits<ExpressionFactor<T> > : public Testable<ExpressionFactor<T> > {};
|
|
|
|
|
2020-07-27 17:57:31 +08:00
|
|
|
/**
|
|
|
|
* N-ary variadic template for ExpressionFactor meant as a base class for N-ary
|
|
|
|
* factors. Enforces an 'expression' method with N keys.
|
|
|
|
* Derived class (an N-factor!) needs to call 'initialize'.
|
|
|
|
*
|
|
|
|
* Does not provide backward compatible 'evaluateError'.
|
|
|
|
*
|
|
|
|
* \tparam T Type for measurements. The rest of template arguments are types
|
|
|
|
* for the N key-indexed Values.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
template <typename T, typename... Args>
|
|
|
|
class ExpressionFactorN : public ExpressionFactor<T> {
|
|
|
|
public:
|
|
|
|
static const std::size_t NARY_EXPRESSION_SIZE = sizeof...(Args);
|
|
|
|
using ArrayNKeys = std::array<Key, NARY_EXPRESSION_SIZE>;
|
2023-01-11 05:07:58 +08:00
|
|
|
|
|
|
|
// Provide access to the Matrix& version of unwhitenedError:
|
2023-01-07 08:29:15 +08:00
|
|
|
using ExpressionFactor<T>::unwhitenedError;
|
2020-07-27 17:57:31 +08:00
|
|
|
|
|
|
|
// Don't provide backward compatible evaluateVector(), due to its problematic
|
|
|
|
// variable length of optional Jacobian arguments. Vector evaluateError(const
|
|
|
|
// Args... args,...);
|
|
|
|
|
|
|
|
/// Recreate expression from given keys_ and measured_, used in load
|
|
|
|
/// Needed to deserialize a derived factor
|
|
|
|
virtual Expression<T> expression(const ArrayNKeys &keys) const {
|
|
|
|
throw std::runtime_error(
|
|
|
|
"ExpressionFactorN::expression not provided: cannot deserialize.");
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
/// Default constructor, for serialization
|
|
|
|
ExpressionFactorN() = default;
|
|
|
|
|
|
|
|
/// Constructor takes care of keys, but still need to call initialize
|
|
|
|
ExpressionFactorN(const ArrayNKeys &keys, const SharedNoiseModel &noiseModel,
|
|
|
|
const T &measurement)
|
|
|
|
: ExpressionFactor<T>(noiseModel, measurement) {
|
|
|
|
for (const auto &key : keys)
|
|
|
|
Factor::keys_.push_back(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// Return an expression that predicts the measurement given Values
|
|
|
|
Expression<T> expression() const override {
|
|
|
|
ArrayNKeys keys;
|
|
|
|
int idx = 0;
|
|
|
|
for (const auto &key : Factor::keys_)
|
|
|
|
keys[idx++] = key;
|
|
|
|
return expression(keys);
|
|
|
|
}
|
|
|
|
|
2023-02-06 11:30:01 +08:00
|
|
|
#ifdef GTSAM_ENABLE_BOOST_SERIALIZATION
|
2020-07-27 17:57:31 +08:00
|
|
|
friend class boost::serialization::access;
|
|
|
|
template <class ARCHIVE>
|
|
|
|
void serialize(ARCHIVE &ar, const unsigned int /*version*/) {
|
|
|
|
ar &boost::serialization::make_nvp(
|
|
|
|
"ExpressionFactorN",
|
|
|
|
boost::serialization::base_object<ExpressionFactor<T>>(*this));
|
|
|
|
}
|
2023-02-06 11:30:01 +08:00
|
|
|
#endif
|
2020-07-27 17:57:31 +08:00
|
|
|
};
|
|
|
|
/// traits
|
|
|
|
template <typename T, typename... Args>
|
2020-07-27 18:04:08 +08:00
|
|
|
struct traits<ExpressionFactorN<T, Args...>>
|
2020-07-27 17:57:31 +08:00
|
|
|
: public Testable<ExpressionFactorN<T, Args...>> {};
|
|
|
|
// ExpressionFactorN
|
|
|
|
|
|
|
|
} // namespace gtsam
|