blob: c1bad9b53fb4354ca709222aee8e805d6386d550 [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
#include "main.h"
#include <Eigen/Dense>
#include <contrib/Eigen/MatrixFunctions>
struct MissingShape {};
struct PartialShape {};
template <int Rows, int Cols>
struct ShapeProbe;
namespace Eigen {
namespace internal {
template <int Rows, int Cols>
struct traits<ShapeProbe<Rows, Cols>> : traits<Matrix<double, Rows, Cols>> {};
template <>
struct traits<PartialShape> {
static constexpr int RowsAtCompileTime = 3;
};
} // namespace internal
} // namespace Eigen
template <int Rows, int Cols>
struct ShapeProbe : MatrixBase<ShapeProbe<Rows, Cols>> {
using Nested = const ShapeProbe&;
mutable int row_reads = 0;
mutable int col_reads = 0;
Index rows() const {
++row_reads;
return 2;
}
Index cols() const {
++col_reads;
return 2;
}
};
void rank_update_shape_checks() {
Matrix2d b = Matrix2d::Constant(2);
Matrix2d updated = Matrix2d::Zero();
updated.selfadjointView<Lower>().rankUpdate(b);
VERIFY_IS_EQUAL(Matrix2d(updated.selfadjointView<Lower>()), (b * b.transpose()).eval());
MatrixXd wrong = MatrixXd::Ones(3, 2);
VERIFY_RAISES_ASSERT(updated.selfadjointView<Lower>().rankUpdate(wrong));
#ifdef EIGEN_NO_STATIC_ASSERT
Matrix<double, 3, 2> fixed_wrong = wrong;
VERIFY_RAISES_ASSERT(updated.selfadjointView<Lower>().rankUpdate(fixed_wrong));
#endif
}
void product_coefficient_shape_checks() {
Matrix2d a = Matrix2d::Identity(), b = Matrix2d::Constant(2);
VERIFY_IS_EQUAL(a.lazyProduct(b).coeff(0, 1), 2);
Vector2d v = Vector2d::Ones();
VERIFY_IS_EQUAL((v.transpose() * v).coeff(0, 0), 2);
VERIFY_IS_EQUAL((v.transpose() * v).coeff(0), 2);
MatrixXd dynamic_a = a, dynamic_b = b;
VERIFY_RAISES_ASSERT((dynamic_a * dynamic_b).coeff(0, 0));
VERIFY_RAISES_ASSERT((dynamic_a * dynamic_b).coeff(0));
MatrixXd scalar = MatrixXd::Constant(1, 1, 3);
VERIFY_IS_EQUAL((scalar * scalar).coeff(0), 9);
}
void transpose_shape_checks() {
Matrix<double, 2, 3> rectangle;
rectangle << 1, 2, 3, 4, 5, 6;
MatrixXd resizable = rectangle;
resizable.transposeInPlace();
VERIFY_IS_EQUAL(resizable, rectangle.transpose());
Matrix<double, 2, Dynamic> partial = Matrix2d::Identity();
partial.transposeInPlace();
VERIFY_IS_EQUAL(partial, Matrix2d::Identity());
partial = rectangle;
VERIFY_RAISES_ASSERT(partial.transposeInPlace());
#ifdef EIGEN_NO_STATIC_ASSERT
VERIFY_RAISES_ASSERT(rectangle.transposeInPlace());
#endif
}
void product_shape_checks() {
transpose_shape_checks();
product_coefficient_shape_checks();
rank_update_shape_checks();
ShapeProbe<2, 2> a, b;
const auto product = a * b;
EIGEN_UNUSED_VARIABLE(product);
VERIFY_IS_EQUAL(a.col_reads + b.row_reads, 0);
const MatrixBase<ShapeProbe<2, 2>>& base_a = a;
const MatrixBase<ShapeProbe<2, 2>>& base_b = b;
EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(base_a, base_b);
VERIFY_IS_EQUAL(a.col_reads + b.row_reads, 0);
Matrix2d fixed = Matrix2d::Identity();
MatrixXd wrong(3, 2);
VERIFY_RAISES_ASSERT((fixed * wrong));
#ifdef EIGEN_NO_STATIC_ASSERT
Matrix<double, 3, 2> fixed_wrong;
VERIFY_RAISES_ASSERT((Product<Matrix2d, decltype(fixed_wrong)>(fixed, fixed_wrong)));
#endif
Transpositions<2> swaps;
swaps.setIdentity();
VERIFY_IS_EQUAL((swaps * fixed).eval(), fixed);
VERIFY_IS_EQUAL((fixed * swaps.transpose()).eval(), fixed);
}
template <int Size, int Order>
void product_destination_shape_checks() {
using MatrixType = Matrix<double, Size, Size, Order>;
const MatrixType identity = MatrixType::Identity(3, 3);
const MatrixType values = MatrixType::Constant(3, 3, 2);
MatrixType result = MatrixType::Zero(3, 3);
result.noalias() += identity * values;
VERIFY_IS_EQUAL(result, values);
result.noalias() -= identity * values;
VERIFY(result.isZero());
result.noalias() += identity.template selfadjointView<Lower>() * values;
VERIFY_IS_EQUAL(result, values);
result.noalias() -= identity.template triangularView<Upper>() * values;
VERIFY(result.isZero());
PermutationMatrix<Size> permutation(3);
permutation.setIdentity();
DiagonalMatrix<double, Size> diagonal(3);
diagonal.diagonal().setConstant(2);
result.noalias() = identity.template triangularView<Lower>() * diagonal;
VERIFY_IS_EQUAL(result, 2 * identity);
result.noalias() = diagonal * identity.template triangularView<Upper>();
VERIFY_IS_EQUAL(result, 2 * identity);
result.noalias() = identity.template selfadjointView<Lower>() * diagonal;
VERIFY_IS_EQUAL(result, 2 * identity);
result.setZero();
const auto scaled = permutation * diagonal;
result += scaled;
VERIFY_IS_EQUAL(result, 2 * identity);
result -= scaled;
VERIFY(result.isZero());
MatrixXd wrong_destination = MatrixXd::Zero(2, 3);
VERIFY_RAISES_ASSERT(wrong_destination.noalias() += identity * values);
VERIFY_RAISES_ASSERT(wrong_destination.noalias() -= identity * values);
VERIFY_RAISES_ASSERT(wrong_destination += scaled);
VERIFY_RAISES_ASSERT(wrong_destination -= scaled);
result.noalias() += scaled * identity;
VERIFY_IS_EQUAL(result, 2 * identity);
result.noalias() -= scaled * identity;
VERIFY(result.isZero());
VERIFY_IS_EQUAL((scaled * permutation).toDenseMatrix(), 2 * identity);
VERIFY_IS_EQUAL((permutation * scaled).toDenseMatrix(), 2 * identity);
VERIFY_IS_EQUAL((permutation * permutation.inverse()).toDenseMatrix().template cast<double>(), identity);
const ScaledPermutationMatrix<double, Size> from_coefficients(permutation.indices(), diagonal.diagonal());
VERIFY_IS_EQUAL(from_coefficients.toDenseMatrix(), 2 * identity);
DiagonalMatrix<double, Dynamic> wrong(2);
VERIFY_RAISES_ASSERT(permutation * wrong);
}
EIGEN_DECLARE_TEST(shape_checks) {
ShapeProbe<2, 2> fixed_a, fixed_b;
const auto sum = fixed_a + fixed_b;
EIGEN_UNUSED_VARIABLE(sum);
internal::resize_if_allowed(fixed_a, fixed_b, internal::add_assign_op<double, double>());
VERIFY_IS_EQUAL(fixed_a.row_reads + fixed_b.row_reads + fixed_a.col_reads + fixed_b.col_reads, 0);
ShapeProbe<2, Dynamic> partial_a, partial_b;
const auto partial_sum = partial_a + partial_b;
EIGEN_UNUSED_VARIABLE(partial_sum);
VERIFY_IS_EQUAL(partial_a.row_reads + partial_b.row_reads, 0);
#ifndef EIGEN_NO_DEBUG
VERIFY(partial_a.col_reads > 0 && partial_b.col_reads > 0);
#endif
Matrix2d matrix = Matrix2d::Identity();
MatrixXd bad(3, 2);
VERIFY_RAISES_ASSERT((matrix + bad));
VERIFY_RAISES_ASSERT(matrix += bad);
VERIFY_IS_EQUAL(hcat(matrix, matrix).cols(), 4);
VERIFY_IS_EQUAL(vcat(matrix, matrix).rows(), 4);
Matrix<double, 2, 3> concat_wide = Matrix<double, 2, 3>::Ones();
VERIFY_IS_EQUAL(hcat(matrix, concat_wide).cols(), 5);
VERIFY_IS_EQUAL(vcat(matrix, concat_wide.transpose()).rows(), 5);
#ifdef EIGEN_NO_STATIC_ASSERT
VERIFY_RAISES_ASSERT(hcat(matrix, concat_wide.transpose()));
VERIFY_RAISES_ASSERT(vcat(matrix, concat_wide));
#endif
Matrix<double, 2, 3> wide = Matrix<double, 2, 3>::Ones();
const DiagonalMatrix<double, 2> left_diagonal(Vector2d::Constant(2));
const DiagonalMatrix<double, 3> right_diagonal(Vector3d::Constant(3));
Matrix<double, 2, 3> triangular_product = left_diagonal * wide.triangularView<Upper>();
VERIFY_IS_EQUAL(triangular_product, 2 * wide.triangularView<Upper>().toDenseMatrix());
triangular_product = wide.triangularView<Upper>() * right_diagonal;
VERIFY_IS_EQUAL(triangular_product, 3 * wide.triangularView<Upper>().toDenseMatrix());
Vector3d vector = Vector3d::Ones();
RowVector3d row = RowVector3d::Ones();
VERIFY_IS_EQUAL(vector.dot(row), 3);
VERIFY_IS_EQUAL(vector.reshaped(fix<1>, fix<3>).sum(), 3);
VectorXd dynamic_reshape = vector;
VERIFY_RAISES_ASSERT(dynamic_reshape.reshaped(fix<2>, fix<2>));
#ifdef EIGEN_NO_STATIC_ASSERT
VERIFY_RAISES_ASSERT(vector.reshaped(fix<2>, fix<2>));
Vector4d mismatched_vector = Vector4d::Ones();
VERIFY_RAISES_ASSERT(vector.dot(mismatched_vector));
#endif
VectorXd bad_vector(2);
VERIFY_RAISES_ASSERT(vector.dot(bad_vector));
VectorXd resized;
resized.resizeLike(row);
VERIFY_IS_EQUAL(resized.size(), 3);
MatrixXd resize_source = matrix;
VERIFY_RAISES_ASSERT(resized.resizeLike(resize_source));
#ifdef EIGEN_NO_STATIC_ASSERT
VERIFY_RAISES_ASSERT(resized.resizeLike(matrix));
#endif
Ref<VectorXd> ref(vector);
Ref<Matrix2d> matrix_ref(matrix);
VERIFY_IS_EQUAL(matrix_ref.rows(), 2);
Matrix2d resized_matrix;
resized_matrix.resizeLike(matrix);
#ifdef EIGEN_NO_STATIC_ASSERT
VERIFY_RAISES_ASSERT(EIGEN_INTERNAL_ASSERT_SCALAR(matrix));
#endif
VERIFY_IS_EQUAL(ref.size(), 3);
MatrixXd dynamic_matrix = matrix;
VERIFY_RAISES_ASSERT((Ref<VectorXd>(dynamic_matrix)));
VERIFY_IS_EQUAL(matrix.determinant(), 1);
VERIFY_IS_EQUAL(matrix.householderQr().absDeterminant(), 1);
VERIFY_IS_EQUAL(matrix.fullPivLu().determinant(), 1);
VERIFY_IS_EQUAL(matrix.fullPivLu().rcond(), 1);
Matrix3d transpose_source = Matrix3d::Random();
const Matrix3d transposed = transpose_source.transpose();
transpose_source.transposeInPlace();
VERIFY_IS_EQUAL(transpose_source, transposed);
MatrixXd rectangular_transpose = MatrixXd::Random(2, 3);
const MatrixXd rectangular_expected = rectangular_transpose.transpose();
rectangular_transpose.transposeInPlace();
VERIFY_IS_EQUAL(rectangular_transpose, rectangular_expected);
VERIFY(matrix.llt().info() == Success);
Vector2d rhs = Vector2d::Ones();
const auto lu = matrix.fullPivLu();
VERIFY_IS_EQUAL(lu.solve(rhs).eval(), rhs);
VERIFY_IS_EQUAL(lu.transpose().solve(rhs).eval(), rhs);
Matrix2d nonsymmetric;
nonsymmetric << 2, 1, 0, 3;
const auto partial_lu = nonsymmetric.partialPivLu();
const Vector2d transpose_solution = partial_lu.transpose().solve(rhs);
VERIFY_IS_APPROX(nonsymmetric.transpose() * transpose_solution, rhs);
VERIFY_RAISES_ASSERT(partial_lu.transpose().solve(VectorXd::Ones(3)));
VERIFY_RAISES_ASSERT(lu.solve(VectorXd::Ones(3)));
#ifdef EIGEN_NO_STATIC_ASSERT
VERIFY_RAISES_ASSERT(lu.solve(Vector3d::Ones()));
#endif
VERIFY_IS_APPROX(matrix.sqrt(), matrix);
VERIFY(SelfAdjointEigenSolver<Matrix2d>(matrix).info() == Success);
MatrixXd rectangle = MatrixXd::Ones(2, 3);
VERIFY_RAISES_ASSERT(rectangle.determinant());
VERIFY_RAISES_ASSERT(rectangle.householderQr().absDeterminant());
VERIFY_RAISES_ASSERT(rectangle.fullPivLu().determinant());
VERIFY_RAISES_ASSERT(rectangle.llt());
VERIFY_RAISES_ASSERT(wide.fullPivLu().determinant());
MatrixXd empty(0, 0);
VERIFY_IS_EQUAL(empty.sum(), 0);
const Matrix<double, 0, 0> fixed_empty;
VERIFY_IS_EQUAL(fixed_empty.sum(), 0);
VERIFY_IS_EQUAL(fixed_empty.prod(), 1);
Matrix<double, 3, 0> empty_columns;
Matrix<double, 0, 3> empty_rows;
VERIFY_IS_EQUAL(empty_columns.colwise().maxCoeff().eval().size(), 0);
VERIFY_IS_EQUAL(empty_rows.rowwise().minCoeff().eval().size(), 0);
VERIFY_RAISES_ASSERT(fixed_empty.maxCoeff());
VERIFY_RAISES_ASSERT(empty_columns.rowwise().maxCoeff());
VERIFY_RAISES_ASSERT(empty_rows.colwise().minCoeff());
VERIFY_RAISES_ASSERT(empty.maxCoeff());
VERIFY_IS_EQUAL(matrix.maxCoeff(), 1);
Index max_row, max_col;
VERIFY_IS_EQUAL(matrix.maxCoeff(&max_row, &max_col), 1);
CALL_SUBTEST(product_shape_checks());
CALL_SUBTEST((product_destination_shape_checks<3, ColMajor>()));
CALL_SUBTEST((product_destination_shape_checks<3, RowMajor>()));
CALL_SUBTEST((product_destination_shape_checks<Dynamic, ColMajor>()));
CALL_SUBTEST((product_destination_shape_checks<Dynamic, RowMajor>()));
using namespace Eigen::internal;
STATIC_CHECK((dimension_relation<3, 3>::Proven));
STATIC_CHECK((!dimension_relation<3, 2>::Compatible));
STATIC_CHECK((dimension_relation<Dynamic, 3>::Compatible));
STATIC_CHECK((!dimension_relation<Dynamic, 3>::Known));
STATIC_CHECK((compile_time_shape<MissingShape>::Rows == Dynamic));
STATIC_CHECK((compile_time_shape<PartialShape>::Rows == 3));
STATIC_CHECK((compile_time_shape<PartialShape>::Cols == Dynamic));
STATIC_CHECK((compile_time_shape<Matrix<double, Dynamic, 1, 0, 3, 1>>::Size == Dynamic));
STATIC_CHECK((matching_sizes<Matrix<double, 2, 3>, Matrix<double, 3, 2>>::Proven));
STATIC_CHECK((matching_shapes<Matrix<double, 1, 0>, Matrix<double, 0, 1>>::Compatible));
const Matrix<double, 1, 0> empty_row;
const Matrix<double, 0, 1> empty_column;
VERIFY_RAISES_ASSERT(empty_row + empty_column);
VERIFY_RAISES_ASSERT(internal::resize_if_allowed(empty_row, empty_column, internal::add_assign_op<double, double>()));
STATIC_CHECK((square_dimensions<const Matrix3d&>::Proven));
int reads = 0;
struct RuntimeDimensions {
mutable int accesses = 0;
Index rows() const {
++accesses;
return 2;
}
Index cols() const {
++accesses;
return 3;
}
} runtime_a, runtime_b;
EIGEN_INTERNAL_ASSERT_SAME_ROWS(runtime_a, runtime_b);
EIGEN_INTERNAL_ASSERT_SAME_COLS(runtime_a, runtime_b);
#ifndef EIGEN_NO_DEBUG
VERIFY_IS_EQUAL(runtime_a.accesses, 2);
VERIFY_IS_EQUAL(runtime_b.accesses, 2);
#endif
EIGEN_INTERNAL_SHAPE_ASSERT((dimension_relation<3, 3>::Proven), ++reads == 1); // NOLINT(bugprone-assert-side-effect)
VERIFY_IS_EQUAL(reads, 0);
EIGEN_INTERNAL_ASSERT_NONEMPTY_SIZE(fixed_a, ++reads); // NOLINT(bugprone-assert-side-effect)
VERIFY_IS_EQUAL(reads, 0);
#ifndef EIGEN_NO_DEBUG
// NOLINTBEGIN(bugprone-assert-side-effect)
EIGEN_INTERNAL_SHAPE_ASSERT((dimension_relation<Dynamic, 3>::Proven), ++reads == 1);
// NOLINTEND(bugprone-assert-side-effect)
VERIFY_IS_EQUAL(reads, 1);
VERIFY_RAISES_ASSERT([] { EIGEN_INTERNAL_SHAPE_ASSERT(false, false); }());
#endif
}