Skip to content

AutoDiffScalar: Failing assert when using dynamic-sized type for derivatives

(Note: This is a duplicate of #1437 (closed) which I cannot reopen due to Gitlab migration)

Summary

The (unsupported) AutoDiffScalar triggers an assertion when used with certain expressions while using dynamic-sized derivative storage.

#1437 (closed) was filed as a dup of #1281 (closed) (which was closed as fixed and the example included there now indeed seems to work), but this example still seems to fail.

Environment

  • Operating System : macOS
  • Architecture : x64
  • Eigen Version : 3.4-rc1
  • Compiler Version : clang 11
  • Compile Flags : -O3 -march=native
  • Vector Extension : AVX512

Minimal Example

#include <iostream>

#include "Eigen/Core"
#include "unsupported/Eigen/AutoDiff"

typedef Eigen::AutoDiffScalar<Eigen::VectorXd> AD;

int main (int argc, char const *argv[])
{
  AD noDer = 1.0; // no (unsized) .derivatives()

  AD hasDer;
  hasDer.value() = 1.0;
  hasDer.derivatives() = Eigen::VectorXd::Unit(5, 0);
  
  std::cout << (2.0 * noDer - hasDer).derivatives().transpose() << std::endl; // assertion failure
  
  return 0;
}

Steps to reproduce

  1. Compile the example with Eigen in the include path (leaving asserts enabled).
  2. Run the example

What is the current bug behavior?

Assertion failed: (aLhs.rows() == aRhs.rows() && aLhs.cols() == aRhs.cols()), function CwiseBinaryOp, file eigen-3.4-587a6915/Eigen/src/Core/CwiseBinaryOp.h, line 116.

What is the expected correct behavior?

I would expect the example to work (or notify me at compile-time if the expression is invalid) and result in the correctly propagated derivatives.

Anything else that might help

I'm hitting a similar issue in real code (which means my Eigen 3.4 benchmarks may be a bit optimistic).

We seem to have have fixed / worked around this in our Eigen 3.2.9 version by implementing make_coherent_impl for (some?) more expressions, e.g.

+template<class UnaryOpLhs, class DerTypeLhs, class UnaryOpRhs, class DerTypeRhs>
+struct make_coherent_impl<CwiseUnaryOp<UnaryOpLhs, DerTypeLhs>, CwiseUnaryOp<UnaryOpRhs, DerTypeRhs> >
+{
+  typedef CwiseUnaryOp<UnaryOpLhs, DerTypeLhs> A;
+  typedef CwiseUnaryOp<UnaryOpRhs, DerTypeRhs> B;
+  static void run(A& a, B& b)
+  {
+    if (B::SizeAtCompileTime==Dynamic && a.size()!=0 && b.size()==0)
+    {
+      make_coherent_expression(b,a);
+    }
+    else if (A::SizeAtCompileTime==Dynamic && b.size()!=0 && a.size()==0)
+    {
+      make_coherent_expression(a,b);
+    }
+  }
+};
+
+template<typename BinaryOp, typename LhsA, typename LhsB, typename RhsA, typename RhsB>
+struct make_coherent_impl<CwiseBinaryOp<BinaryOp, LhsA, LhsB>, CwiseBinaryOp<BinaryOp, RhsA, RhsB> >
+{
+  typedef CwiseBinaryOp<BinaryOp, LhsA, LhsB> A;
+  typedef CwiseBinaryOp<BinaryOp, RhsA, RhsB> B;
+  static void run(A& a, B& b)
+  {
+    if (B::SizeAtCompileTime==Dynamic && a.size()!=0 && b.size()==0)
+    {
+      make_coherent_expression(b,a);
+    }
+    else if (A::SizeAtCompileTime==Dynamic && b.size()!=0 && a.size()==0)
+    {
+      make_coherent_expression(a,b);
+    }
+  }
+};
+
+template<typename BinaryOp, typename UnaryOp, typename LhsA, typename LhsB, typename DerTypeRhs>
+struct make_coherent_impl<CwiseBinaryOp<BinaryOp, LhsA, LhsB>, CwiseUnaryOp<UnaryOp, DerTypeRhs> >
+{
+  typedef CwiseBinaryOp<BinaryOp, LhsA, LhsB> A;
+  typedef CwiseUnaryOp<UnaryOp, DerTypeRhs> B;
+  static void run(A& a, B& b)
+  {
+    if (B::SizeAtCompileTime==Dynamic && a.size()!=0 && b.size()==0)
+    {
+      make_coherent_expression(b,a);
+    }
+    else if (A::SizeAtCompileTime==Dynamic && b.size()!=0 && a.size()==0)
+    {
+      make_coherent_expression(a,b);
+    }
+  }
+};
+
+template<typename BinaryOp, typename UnaryOpRhs, typename DerTypeLhs, typename RhsA, typename RhsB>
+struct make_coherent_impl<CwiseUnaryOp<UnaryOpRhs, DerTypeLhs>, CwiseBinaryOp<BinaryOp, RhsA, RhsB> >
+{
+  typedef CwiseUnaryOp<UnaryOpRhs, DerTypeLhs> A;
+  typedef CwiseBinaryOp<BinaryOp, RhsA, RhsB> B;
+  static void run(A& a, B& b)
+  {
+    if (B::SizeAtCompileTime==Dynamic && a.size()!=0 && b.size()==0)
+    {
+      make_coherent_expression(b,a);
+    }
+    else if (A::SizeAtCompileTime==Dynamic && b.size()!=0 && a.size()==0)
+    {
+      make_coherent_expression(a,b);
+    }
+  }
+};
+
+template<typename UnaryOp, typename DerTypeLhs, typename Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
+struct make_coherent_impl<CwiseUnaryOp<UnaryOp, DerTypeLhs>, Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> >
+{
+  typedef CwiseUnaryOp<UnaryOp, DerTypeLhs> A;
+  typedef Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> B;
+  static void run(A& a, B& b)
+  {
+    if (B::SizeAtCompileTime==Dynamic && a.size()!=0 && b.size()==0)
+    {
+      b.resize(a.size());
+      b.setZero();
+    }
+    else if (A::SizeAtCompileTime==Dynamic && b.size()!=0 && a.size()==0)
+    {
+      make_coherent_expression(a,b);
+    }
+  }
+};
+
+template<typename Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols, typename UnaryOp, typename DerTypeRhs>
+struct make_coherent_impl<Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>, CwiseUnaryOp<UnaryOp, DerTypeRhs> >
+{
+  typedef Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> A;
+  typedef CwiseUnaryOp<UnaryOp, DerTypeRhs> B;
+  static void run(A& a, B& b)
+  {
+    if (B::SizeAtCompileTime==Dynamic && a.size()!=0 && b.size()==0)
+    {
+      make_coherent(b,a);
+    }
+    else if (A::SizeAtCompileTime==Dynamic && b.size()!=0 && a.size()==0)
+    {
+      a.resize(b.size());
+      a.setZero();
+    }
+  }
+};
+
+template<typename BinaryOp, typename LhsA, typename LhsB, typename Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
+struct make_coherent_impl<CwiseBinaryOp<BinaryOp, LhsA, LhsB>, Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> >
+{
+  typedef CwiseBinaryOp<BinaryOp, LhsA, LhsB> A;
+  typedef Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> B;
+  static void run(A& a, B& b)
+  {
+    if (B::SizeAtCompileTime==Dynamic && a.size()!=0 && b.size()==0)
+    {
+      b.resize(a.size());
+      b.setZero();
+    }
+    else if (A::SizeAtCompileTime==Dynamic && b.size()!=0 && a.size()==0)
+    {
+      make_coherent_expression(a,b);
+    }
+  }
+};
+
+template<typename BinaryOp, typename RhsA, typename RhsB, typename Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
+struct make_coherent_impl<Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>, CwiseBinaryOp<BinaryOp, RhsA, RhsB> >
+{
+  typedef Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> A;
+  typedef CwiseBinaryOp<BinaryOp, RhsA, RhsB> B;
+  static void run(A& a, B& b)
+  {
+    if (B::SizeAtCompileTime==Dynamic && a.size()!=0 && b.size()==0)
+    {
+      make_coherent_expression(b,a);
+    }
+    else if (A::SizeAtCompileTime==Dynamic && b.size()!=0 && a.size()==0)
+    {
+      a.resize(b.size());
+      a.setZero();
+    }
+  }
+};