Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eigen NumTraits incomplete for Eigen/Geometry/AngleAxis #310

Closed
karstenkrispin opened this issue Aug 17, 2017 · 12 comments
Closed

Eigen NumTraits incomplete for Eigen/Geometry/AngleAxis #310

karstenkrispin opened this issue Aug 17, 2017 · 12 comments

Comments

@karstenkrispin
Copy link

karstenkrispin commented Aug 17, 2017

Hello,

when using Eigen's Geometry/AngleAxis class, compilation errors occour, as these classes require the traits highest() and lowest() to be defined.

  static inline Real highest() {
    return Real(std::numeric_limits<T>::max());
  }
  static inline Real lowest() {
    return Real(-std::numeric_limits<T>::max());
  }

Would be nice, if they could be added.

Cheers

@pmoulon
Copy link
Contributor

pmoulon commented Aug 17, 2017

Do you mean?

static inline Real lowest() {
    return Real(-std::numeric_limits<T>::max());
  }
#include <limits>
#include <cstddef>
#include <iostream>
 
int main()
{
    std::cout 
        << "float: " << std::defaultfloat << std::numeric_limits<float>::min()
        << " or " << std::defaultfloat << std::numeric_limits<float>::lowest()
        << " or " << std::defaultfloat << -std::numeric_limits<float>::max() << '\n';
}

=> float: 1.17549e-38 or -3.40282e+38 or -3.40282e+38

@karstenkrispin
Copy link
Author

karstenkrispin commented Aug 17, 2017

@pmoulon you are right. its either

std::numeric_limits<float>::lowest() or -std::numeric_limits<float>::max()

as described by NumTrait Docs

and... sorry for closing this issue prematurely...

@keir
Copy link
Contributor

keir commented Aug 24, 2017

Can you clarify what Eigen version you are using?

@karstenkrispin
Copy link
Author

Eigen version is 3.3.4

@keir
Copy link
Contributor

keir commented Aug 25, 2017

Can you please post a complete compilable example so I can reproduce the issue?

@keir
Copy link
Contributor

keir commented Aug 25, 2017

Also, have you seen ceres/rotation.h? Using Jet objects in Eigen's angle axis implementations is probably not a good idea; we have special handling for derivatives that won't be done in Eigen's version.

@karstenkrispin
Copy link
Author

karstenkrispin commented Aug 28, 2017

@keir
yes, I know about ceres/rotation.h.
However, I would like formulate my terms as much as possible with Eigen Expressions only.

I found that the reason for the compilation error stems from using the initialization from a rotation matrix AngleAxis(Eigen::MatrixBase). This then leads to problems found in Quaternions.

I apologize here that my bugreport is overall sloppy... :(

Will come up with an "non-working" example soon.

Then, using g++-7.1, the error will be

In file included from /usr/include/eigen3/Eigen/Geometry:42:0,
                 from /usr/include/eigen3/Eigen/Dense:6,
                 from /usr/include/ceres/internal/numeric_diff.h:40,
                 from /usr/include/ceres/dynamic_numeric_diff_cost_function.h:44,
                 from /usr/include/ceres/ceres.h:44,
                 from eigen_autodiff.cpp:2:
/usr/include/eigen3/Eigen/src/Geometry/Quaternion.h: In instantiation of 'Derived& Eigen::QuaternionBase<Derived>::operator=(const Eigen::MatrixBase<OtherDerived>&) [with OtherDerived = Eigen::Map<const Eigen::Matrix<double, 3, 1>, 0, Eigen::Stride<0, 0> >; Derived = Eigen::Quaternion<double>]':
/usr/include/eigen3/Eigen/src/Geometry/Quaternion.h:267:90:   required from 'Eigen::Quaternion<Scalar, Options>::Quaternion(const Eigen::MatrixBase<OtherDerived>&) [with Derived = Eigen::Map<const Eigen::Matrix<double, 3, 1>, 0, Eigen::Stride<0, 0> >; _Scalar = double; int _Options = 0]'
/usr/include/eigen3/Eigen/src/Geometry/AngleAxis.h:201:18:   required from 'Eigen::AngleAxis<_Scalar>& Eigen::AngleAxis<Scalar>::operator=(const Eigen::MatrixBase<OtherDerived>&) [with Derived = Eigen::Map<const Eigen::Matrix<double, 3, 1>, 0, Eigen::Stride<0, 0> >; _Scalar = double]'
/usr/include/eigen3/Eigen/src/Geometry/AngleAxis.h:88:85:   required from 'Eigen::AngleAxis<Scalar>::AngleAxis(const Eigen::MatrixBase<OtherDerived>&) [with Derived = Eigen::Map<const Eigen::Matrix<double, 3, 1>, 0, Eigen::Stride<0, 0> >; _Scalar = double]'
eigen_autodiff.cpp:27:16:   required from 'bool RotationTerm::operator()(const T*, T*) const [with T = double]'
/usr/include/ceres/internal/variadic_evaluate.h:175:19:   required from 'static bool ceres::internal::VariadicEvaluate<Functor, T, N0, 0, 0, 0, 0, 0, 0, 0, 0, 0>::Call(const Functor&, const T* const*, T*) [with Functor = RotationTerm; T = double; int N0 = 3]'
/usr/include/ceres/autodiff_cost_function.h:208:17:   required from 'bool ceres::AutoDiffCostFunction<CostFunctor, kNumResiduals, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Evaluate(const double* const*, double*, double**) const [with CostFunctor = RotationTerm; int kNumResiduals = 3; int N0 = 3; int N1 = 0; int N2 = 0; int N3 = 0; int N4 = 0; int N5 = 0; int N6 = 0; int N7 = 0; int N8 = 0; int N9 = 0]'
eigen_autodiff.cpp:54:1:   required from here
/usr/include/eigen3/Eigen/src/Geometry/Quaternion.h:522:59: error: incomplete type 'Eigen::internal::quaternionbase_assign_impl<Eigen::Map<const Eigen::Matrix<double, 3, 1>, 0, Eigen::Stride<0, 0> >, 3, 1>' used in nested name specifier
   internal::quaternionbase_assign_impl<MatrixDerived>::run(*this, xpr.derived());
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Geometry/Quaternion.h: In instantiation of 'Derived& Eigen::QuaternionBase<Derived>::operator=(const Eigen::MatrixBase<OtherDerived>&) [with OtherDerived = Eigen::Map<const Eigen::Matrix<ceres::Jet<double, 3>, 3, 1, 0, 3, 1>, 0, Eigen::Stride<0, 0> >; Derived = Eigen::Quaternion<ceres::Jet<double, 3>, 0>]':
/usr/include/eigen3/Eigen/src/Geometry/Quaternion.h:267:90:   required from 'Eigen::Quaternion<Scalar, Options>::Quaternion(const Eigen::MatrixBase<OtherDerived>&) [with Derived = Eigen::Map<const Eigen::Matrix<ceres::Jet<double, 3>, 3, 1, 0, 3, 1>, 0, Eigen::Stride<0, 0> >; _Scalar = ceres::Jet<double, 3>; int _Options = 0]'
/usr/include/eigen3/Eigen/src/Geometry/AngleAxis.h:201:18:   required from 'Eigen::AngleAxis<_Scalar>& Eigen::AngleAxis<Scalar>::operator=(const Eigen::MatrixBase<OtherDerived>&) [with Derived = Eigen::Map<const Eigen::Matrix<ceres::Jet<double, 3>, 3, 1, 0, 3, 1>, 0, Eigen::Stride<0, 0> >; _Scalar = ceres::Jet<double, 3>]'
/usr/include/eigen3/Eigen/src/Geometry/AngleAxis.h:88:85:   required from 'Eigen::AngleAxis<Scalar>::AngleAxis(const Eigen::MatrixBase<OtherDerived>&) [with Derived = Eigen::Map<const Eigen::Matrix<ceres::Jet<double, 3>, 3, 1, 0, 3, 1>, 0, Eigen::Stride<0, 0> >; _Scalar = ceres::Jet<double, 3>]'
eigen_autodiff.cpp:27:16:   required from 'bool RotationTerm::operator()(const T*, T*) const [with T = ceres::Jet<double, 3>]'
/usr/include/ceres/internal/variadic_evaluate.h:175:19:   required from 'static bool ceres::internal::VariadicEvaluate<Functor, T, N0, 0, 0, 0, 0, 0, 0, 0, 0, 0>::Call(const Functor&, const T* const*, T*) [with Functor = RotationTerm; T = ceres::Jet<double, 3>; int N0 = 3]'
/usr/include/ceres/internal/autodiff.h:282:72:   required from 'static bool ceres::internal::AutoDiff<Functor, T, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Differentiate(const Functor&, const T* const*, int, T*, T**) [with Functor = RotationTerm; T = double; int N0 = 3; int N1 = 0; int N2 = 0; int N3 = 0; int N4 = 0; int N5 = 0; int N6 = 0; int N7 = 0; int N8 = 0; int N9 = 0]'
/usr/include/ceres/autodiff_cost_function.h:211:66:   required from 'bool ceres::AutoDiffCostFunction<CostFunctor, kNumResiduals, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Evaluate(const double* const*, double*, double**) const [with CostFunctor = RotationTerm; int kNumResiduals = 3; int N0 = 3; int N1 = 0; int N2 = 0; int N3 = 0; int N4 = 0; int N5 = 0; int N6 = 0; int N7 = 0; int N8 = 0; int N9 = 0]'
eigen_autodiff.cpp:54:1:   required from here
/usr/include/eigen3/Eigen/src/Geometry/Quaternion.h:522:59: error: incomplete type 'Eigen::internal::quaternionbase_assign_impl<Eigen::Map<const Eigen::Matrix<ceres::Jet<double, 3>, 3, 1, 0, 3, 1>, 0, Eigen::Stride<0, 0> >, 3, 1>' used in nested name specifier
In file included from /usr/include/eigen3/Eigen/Core:446:0,
                 from /usr/include/ceres/jet.h:165,
                 from /usr/include/ceres/internal/autodiff.h:145,
                 from /usr/include/ceres/autodiff_cost_function.h:132,
                 from /usr/include/ceres/ceres.h:37,
                 from eigen_autodiff.cpp:2:
/usr/include/eigen3/Eigen/src/Core/StableNorm.h: In instantiation of 'void Eigen::internal::stable_norm_kernel(const ExpressionType&, Scalar&, Scalar&, Scalar&) [with ExpressionType = Eigen::VectorBlock<const Eigen::Block<const Eigen::Matrix<ceres::Jet<double, 3>, 4, 1, 0, 4, 1>, 3, 1, false>, -1>; Scalar = ceres::Jet<double, 3>]':
/usr/include/eigen3/Eigen/src/Core/StableNorm.h:185:33:   required from 'typename Eigen::NumTraits<typename Eigen::internal::traits<T>::Scalar>::Real Eigen::MatrixBase<Derived>::stableNorm() const [with Derived = Eigen::Block<const Eigen::Matrix<ceres::Jet<double, 3>, 4, 1, 0, 4, 1>, 3, 1, false>; typename Eigen::NumTraits<typename Eigen::internal::traits<T>::Scalar>::Real = ceres::Jet<double, 3>]'
/usr/include/eigen3/Eigen/src/Geometry/AngleAxis.h:176:7:   required from 'Eigen::AngleAxis<_Scalar>& Eigen::AngleAxis<Scalar>::operator=(const Eigen::QuaternionBase<OtherDerived>&) [with QuatDerived = Eigen::Quaternion<ceres::Jet<double, 3>, 0>; _Scalar = ceres::Jet<double, 3>]'
/usr/include/eigen3/Eigen/src/Geometry/AngleAxis.h:201:16:   required from 'Eigen::AngleAxis<_Scalar>& Eigen::AngleAxis<Scalar>::operator=(const Eigen::MatrixBase<OtherDerived>&) [with Derived = Eigen::Map<const Eigen::Matrix<ceres::Jet<double, 3>, 3, 1, 0, 3, 1>, 0, Eigen::Stride<0, 0> >; _Scalar = ceres::Jet<double, 3>]'
/usr/include/eigen3/Eigen/src/Geometry/AngleAxis.h:88:85:   required from 'Eigen::AngleAxis<Scalar>::AngleAxis(const Eigen::MatrixBase<OtherDerived>&) [with Derived = Eigen::Map<const Eigen::Matrix<ceres::Jet<double, 3>, 3, 1, 0, 3, 1>, 0, Eigen::Stride<0, 0> >; _Scalar = ceres::Jet<double, 3>]'
eigen_autodiff.cpp:27:16:   required from 'bool RotationTerm::operator()(const T*, T*) const [with T = ceres::Jet<double, 3>]'
/usr/include/ceres/internal/variadic_evaluate.h:175:19:   required from 'static bool ceres::internal::VariadicEvaluate<Functor, T, N0, 0, 0, 0, 0, 0, 0, 0, 0, 0>::Call(const Functor&, const T* const*, T*) [with Functor = RotationTerm; T = ceres::Jet<double, 3>; int N0 = 3]'
/usr/include/ceres/internal/autodiff.h:282:72:   required from 'static bool ceres::internal::AutoDiff<Functor, T, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Differentiate(const Functor&, const T* const*, int, T*, T**) [with Functor = RotationTerm; T = double; int N0 = 3; int N1 = 0; int N2 = 0; int N3 = 0; int N4 = 0; int N5 = 0; int N6 = 0; int N7 = 0; int N8 = 0; int N9 = 0]'
/usr/include/ceres/autodiff_cost_function.h:211:66:   required from 'bool ceres::AutoDiffCostFunction<CostFunctor, kNumResiduals, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Evaluate(const double* const*, double*, double**) const [with CostFunctor = RotationTerm; int kNumResiduals = 3; int N0 = 3; int N1 = 0; int N2 = 0; int N3 = 0; int N4 = 0; int N5 = 0; int N6 = 0; int N7 = 0; int N8 = 0; int N9 = 0]'
eigen_autodiff.cpp:54:1:   required from here
/usr/include/eigen3/Eigen/src/Core/StableNorm.h:26:40: error: 'highest' is not a member of 'Eigen::NumTraits<ceres::Jet<double, 3> >'
     if(tmp > NumTraits<Scalar>::highest())
              ~~~~~~~~~~~~~~~~~~~~~~~~~~^~
/usr/include/eigen3/Eigen/src/Core/StableNorm.h:28:44: error: 'highest' is not a member of 'Eigen::NumTraits<ceres::Jet<double, 3> >'
       invScale = NumTraits<Scalar>::highest();
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~^~
/usr/include/eigen3/Eigen/src/Core/StableNorm.h:31:48: error: 'highest' is not a member of 'Eigen::NumTraits<ceres::Jet<double, 3> >'
     else if(maxCoeff>NumTraits<Scalar>::highest()) // we got a INF

@karstenkrispin
Copy link
Author

karstenkrispin commented Sep 5, 2017

Here is the non-compiling example. The problem indeed stems from the conversion of a rotation matrix to angle axis format.

#include <ceres/ceres.h>

#include <Eigen/Dense>
#include <Eigen/Geometry>

template<typename T>
using Vec = Eigen::Matrix<T,3,1>;

struct RotationTerm {

    template<typename T>
    bool operator()(const T * const angleaxisptr, T * resptr) const
    {

        Vec<T> angleaxis = Eigen::Map<const Vec<T>>(angleaxisptr);
        Eigen::Map<Vec<T>> res(resptr);

        // here it brakes
        auto aaObject = Eigen::AngleAxis<T>(Eigen::Matrix<T,3,3>::Identity());

        res.setZero();
        return true;
    }
};


int main(int argc, char **argv) 
{

    Vec<double> rotation;
    rotation.setZero();

    ceres::Problem problem;

    typedef ceres::AutoDiffCostFunction<RotationTerm, 3, 3> RotationError;

    auto cost_function = new RotationError(new RotationTerm);
    problem.AddResidualBlock(cost_function, NULL, rotation.data());

    ceres::Solver::Options options;
    ceres::Solver::Summary summary;

    ceres::Solve(options, &problem, &summary);

    return 0;
}

cmake project file:

cmake_minimum_required(VERSION 2.8)
project(angleaxis CXX)

find_package(Ceres)

add_executable(${PROJECT_NAME} "eigen_autodiff.cpp")

target_include_directories(${PROJECT_NAME} PUBLIC ${CERES_INCLUDE_DIRS})

target_include_directories(${PROJECT_NAME} PUBLIC ${EIGEN_INCLUDE_DIR})
target_include_directories(${PROJECT_NAME} PUBLIC ${EIGEN3_INCLUDE_DIR})
target_include_directories(${PROJECT_NAME} PUBLIC ${EIGEN_INCLUDE_DIRS})
target_include_directories(${PROJECT_NAME} PUBLIC ${EIGEN3_INCLUDE_DIRS})

target_link_libraries(${PROJECT_NAME} ceres)

@keir
Copy link
Contributor

keir commented Sep 5, 2017

Unfortunately your desire to stay with Eigen expressions will likely lead to bad optimization - autodiff is not a panacea; you cannot blindly use it without understanding what happens underneath. Eigen will not do any special handling for autodiff, nor can they-- the special handling requires digging directly into the autodiff types. It would be strange for Eigen to have Ceres::Jet specific code.

If anything, I'm tempted to add a compile assert to prevent people from using Eigen's AxisAngle code with Jets, since it will lead to tears and frustration. What's most vexing is that if we fixed the current issue to enable Eigen::AngleAxis with Ceres jets, the resulting code would compile, and even work sometimes, but then break silently when near the areas that have the singularities.

What is the reason to not use ceres/rotation.h? It has taken multiple years to get the corner cases right in that code, so I would like to understand the reason not to benefit from our pain and suffering.

@keir
Copy link
Contributor

keir commented Sep 5, 2017

However, I could be convinced to add overloads for Eigen::AngleAxis<> that call into the other routines in ceres/rotation.h. I suspect it'd be messy and not worth the effort, but if the patch turns out reasonably clean, then I would support inclusion in Ceres.

@sandwichmaker
Copy link
Contributor

we should add these traits. It is the user's choice as to how they want to use Jets.

@sandwichmaker
Copy link
Contributor

https://ceres-solver-review.googlesource.com/#/c/ceres-solver/+/11020/ is out for review. Please test it and let me know that it does the needful for you.

keir pushed a commit that referenced this issue Sep 27, 2017
Add highest and lowest traits to the Jet implementation.

#310

Change-Id: I7c68fa8e2baa7742880d3faa21f366352e48aacf
mystorm16 pushed a commit to mystorm16/ceres-solver that referenced this issue May 21, 2024
Add highest and lowest traits to the Jet implementation.

ceres-solver/ceres-solver#310

Change-Id: I7c68fa8e2baa7742880d3faa21f366352e48aacf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants