LU: Let FullPivLU accept a matrix with zero columns

libeigen/eigen!2990

Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
diff --git a/Eigen/src/LU/FullPivLU.h b/Eigen/src/LU/FullPivLU.h
index 3c0aad3..13dca4d 100644
--- a/Eigen/src/LU/FullPivLU.h
+++ b/Eigen/src/LU/FullPivLU.h
@@ -362,7 +362,10 @@
   eigen_assert(m_lu.rows() <= NumTraits<PermutationIndex>::highest() &&
                m_lu.cols() <= NumTraits<PermutationIndex>::highest());
 
-  m_l1_norm = m_lu.cwiseAbs().colwise().sum().maxCoeff();
+  if (m_lu.cols() > 0)
+    m_l1_norm = m_lu.cwiseAbs().colwise().sum().maxCoeff();
+  else
+    m_l1_norm = RealScalar(0);
 
   const Index size = m_lu.diagonalSize();
   const Index rows = m_lu.rows();
diff --git a/test/lu.cpp b/test/lu.cpp
index a4c7512..17675d1 100644
--- a/test/lu.cpp
+++ b/test/lu.cpp
@@ -165,6 +165,33 @@
   VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
 }
 
+// Regression test: FullPivLU took the maximum of an empty column-sum vector when computing its l1 norm,
+// so it could not be constructed at all from a matrix with zero columns. PartialPivLU already guarded the
+// same reduction.
+template <typename MatrixType>
+void lu_empty() {
+  typedef typename MatrixType::Scalar Scalar;
+  const Index n = 5;
+
+  FullPivLU<MatrixType> lu{MatrixType(0, 0)};
+  VERIFY_IS_EQUAL(lu.rank(), Index(0));
+  VERIFY(lu.isInvertible());
+  VERIFY_IS_EQUAL(lu.determinant(), Scalar(1));
+
+  lu.compute(MatrixType(0, n));
+  VERIFY_IS_EQUAL(lu.rank(), Index(0));
+  VERIFY_IS_EQUAL(lu.dimensionOfKernel(), n);
+  VERIFY(!lu.isInjective());
+
+  lu.compute(MatrixType(n, 0));
+  VERIFY_IS_EQUAL(lu.rank(), Index(0));
+  VERIFY_IS_EQUAL(lu.dimensionOfKernel(), Index(0));
+  VERIFY(!lu.isSurjective());
+
+  PartialPivLU<MatrixType> plu{MatrixType(0, 0)};
+  VERIFY_IS_EQUAL(plu.determinant(), Scalar(1));
+}
+
 template <typename MatrixType>
 void lu_verify_assert() {
   MatrixType tmp;
@@ -302,6 +329,7 @@
     CALL_SUBTEST_4(lu_invertible<MatrixXd>());
     CALL_SUBTEST_4(lu_partial_piv<MatrixXd>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE)));
     CALL_SUBTEST_4(lu_verify_assert<MatrixXd>());
+    CALL_SUBTEST_4(lu_empty<MatrixXd>());
 
     CALL_SUBTEST_5(lu_non_invertible<MatrixXcf>());
     CALL_SUBTEST_5(lu_invertible<MatrixXcf>());
@@ -311,6 +339,7 @@
     CALL_SUBTEST_6(lu_invertible<MatrixXcd>());
     CALL_SUBTEST_6(lu_partial_piv<MatrixXcd>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE)));
     CALL_SUBTEST_6(lu_verify_assert<MatrixXcd>());
+    CALL_SUBTEST_6(lu_empty<MatrixXcd>());
 
     CALL_SUBTEST_7((lu_non_invertible<Matrix<float, Dynamic, 16> >()));