Собственная норма () с Boost.Units

Я пытаюсь использовать Boost.Units с Eigen 3.3.1, но, следуя приведенным здесь инструкциям и обнаружив некоторую информацию, я все еще не могу понять, как заставить работать norm ().

Вот что у меня есть (извините за длинный блок кода):

#include <boost/units/quantity.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/area.hpp>
#include <boost/units/cmath.hpp>
#include <Eigen/Geometry>

namespace Eigen {

//specialization of numeric traits
using boost::units::quantity;
template <typename Unit, typename Scalar>
struct NumTraits<quantity<Unit, Scalar>>
        : GenericNumTraits<quantity<Unit, Scalar>>
{
    typedef quantity<Unit, Scalar> Real;
    typedef quantity<Unit, Scalar> NonInteger;
    typedef quantity<Unit, Scalar> Nested;
    typedef quantity<Unit, Scalar> Literal;

    static inline Real epsilon() { return quantity<Unit, Scalar>(0); }
    static inline Real dummy_precision() { return quantity<Unit, Scalar>(1e-6 * Unit()); }
    static inline Real digits10() { return quantity<Unit, Scalar>(0); }

    enum {
        IsComplex = 0,
        IsInteger = 0,
        IsSigned = 1,
        RequireInitialization = 1,
        ReadCost = 1,
        AddCost = 3,
        MulCost = 3
    };
};

//specialization of sum operator
template <typename Unit, typename Scalar>
struct ScalarBinaryOpTraits<quantity<Unit, Scalar>, quantity<Unit, Scalar>, internal::scalar_sum_op<quantity<Unit, Scalar>, quantity<Unit, Scalar>>> {
    typedef typename boost::units::add_typeof_helper<quantity<Unit, Scalar>, quantity<Unit, Scalar>>::type ReturnType;
};

//specialization of product operator
template <typename Unit, typename Scalar>
struct ScalarBinaryOpTraits<Scalar, quantity<Unit, Scalar>,internal::scalar_product_op<Scalar, quantity<Unit, Scalar>>> {
    typedef Scalar X;
    typedef quantity<Unit, Scalar> Y;
    typedef typename boost::units::multiply_typeof_helper<X, Y>::type ReturnType;
};
template <typename Unit, typename Scalar>
struct ScalarBinaryOpTraits<quantity<Unit, Scalar>, Scalar, internal::scalar_product_op<quantity<Unit, Scalar>, Scalar>> {
    typedef quantity<Unit, Scalar> X;
    typedef Scalar Y;
    typedef typename boost::units::multiply_typeof_helper<X, Y>::type ReturnType;
};
template <typename Unit, typename Scalar>
struct ScalarBinaryOpTraits<quantity<Unit, Scalar>, quantity<Unit, Scalar>, internal::scalar_product_op<quantity<Unit, Scalar>, quantity<Unit, Scalar>>> {
    typedef quantity<Unit, Scalar> X;
    typedef quantity<Unit, Scalar> Y;
    typedef typename boost::units::multiply_typeof_helper<X, Y>::type ReturnType;
};

namespace internal {

//specialization for abs2()
template<typename Unit, typename Scalar>
struct abs2_impl<quantity<Unit, Scalar>>
{
    typedef quantity<Unit, Scalar> X;
    typedef quantity<Unit, Scalar> Y;
    typedef typename boost::units::multiply_typeof_helper<X, Y>::type ReturnType;

    EIGEN_DEVICE_FUNC
    static inline ReturnType run(const quantity<Unit, Scalar>& x)
    {
        return x * x;
    }
};


} // namespace internal

} // namespace Eigen

namespace boost {

namespace units {

//required functions
using namespace boost::units::si;
inline quantity<area, double> abs2(const quantity<length, double>& x)  { return x * x; }

} // namespace units

} // namespace boost

int main(int /*argc*/, char** /*argv[]*/)
{
    //unit typedefs
    using namespace boost::units;
    using namespace boost::units::si;
    using Length = quantity<length, double>;
    using Area = quantity<area, double>;

    //eigen typedefs
    using LengthVector = Eigen::Matrix<Length, 3, 1>;
    using AreaVector = Eigen::Matrix<Area, 3, 1>;
    using LengthMatrix = Eigen::Matrix<Length, 3, 3>;

    //test norm
    LengthVector vector1;
    Length result4 = vector1.norm();
}

Но это не удается скомпилировать (gcc 5.4.0) с ошибкой вроде

не удалось преобразовать "boost::units::sqrt... (некоторая нешифруемая ошибка шаблона)"

а также

не удалось преобразовать "Eigen::internal::abs2_impl... (некоторая неясная ошибка шаблона)"

1 ответ

template<typename Derived>
EIGEN_STRONG_INLINE
typename NumTraits<typename internal::traits<Derived>::Scalar>::Real
MatrixBase<Derived>::squaredNorm() const
{
  return numext::real((*this).cwiseAbs2().sum());
}

template<typename Derived>
inline typename NumTraits<typename internal::traits<Derived>::Scalar>::Real
MatrixBase<Derived>::norm() const
{
     return numext::sqrt(squaredNorm());
}

Как показано выше, Eigen3 norm() функция использует squaredNorm(), Тем не менее, декларация squaredNorm() требует, чтобы возвращаемый тип был таким же, как тип элемента Matrix Derived, что означает, что единицы возвращаемого значения должны совпадать с единицами матричного элемента. Например, вектор смещения единиц измерения, его squaredNorm должен возвращать значение с единицей meter_squared, что противоречит объявлению. Так что может быть невозможно использовать squaredNorm() или же norm() напрямую без изменения реализации Eigen.

Моя идея состоит в том, чтобы написать функцию полезности за пределами Eigen для реализации squaredNorm(), norm() а также normalized():

template<typename T, int Row, int Col>
EIGEN_STRONG_INLINE
static T norm(const Eigen::Matrix<T, Row, Col>& m)
{
     return Eigen::numext::sqrt(squared_norm(m));
}

https://github.com/iastate-robotics/eigen3-units

Другие вопросы по тегам