blob: 1d7dc38161ed9901415f071df4bfaa7478351a9f [file]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
#ifndef EIGEN_SCALEDPERMUTATIONMATRIX_H
#define EIGEN_SCALEDPERMUTATIONMATRIX_H
// IWYU pragma: private
#include "./InternalHeaderCheck.h"
namespace Eigen {
namespace internal {
template <typename Scalar_, int SizeAtCompileTime, int MaxSizeAtCompileTime, typename StorageIndex_>
struct traits<ScaledPermutationMatrix<Scalar_, SizeAtCompileTime, MaxSizeAtCompileTime, StorageIndex_>> {
using Scalar = Scalar_;
using StorageIndex = StorageIndex_;
// Dense, so that products and sums with dense operands promote with the existing rules; the O(n) kernels are
// selected by the shape (ScaledPermutationShape), as for TriangularView.
using StorageKind = Dense;
using XprKind = MatrixXpr;
using PermutationType = PermutationMatrix<SizeAtCompileTime, MaxSizeAtCompileTime, StorageIndex_>;
using ScalesType = Matrix<Scalar_, SizeAtCompileTime, 1, 0, MaxSizeAtCompileTime, 1>;
static constexpr int RowsAtCompileTime = SizeAtCompileTime;
static constexpr int ColsAtCompileTime = SizeAtCompileTime;
static constexpr int MaxRowsAtCompileTime = MaxSizeAtCompileTime;
static constexpr int MaxColsAtCompileTime = MaxSizeAtCompileTime;
static constexpr unsigned int Flags = NestByRefBit;
};
/** \internal The scaled permutation produced by a permutation-like and a diagonal-like operand. */
template <typename Scalar, typename Lhs, typename Rhs, typename StorageIndex>
struct scaled_permutation_result {
static constexpr int Size = size_prefer_fixed(traits<Lhs>::RowsAtCompileTime, traits<Rhs>::RowsAtCompileTime);
static constexpr int MaxSize =
min_size_prefer_fixed(traits<Lhs>::MaxRowsAtCompileTime, traits<Rhs>::MaxRowsAtCompileTime);
using type = ScaledPermutationMatrix<Scalar, Size, MaxSize, StorageIndex>;
};
template <typename Lhs, typename Rhs, typename StorageIndex>
struct scaled_permutation_product_result {
using LhsScalar = typename traits<Lhs>::Scalar;
using RhsScalar = typename traits<Rhs>::Scalar;
using Scalar =
typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_product_op<LhsScalar, RhsScalar>>::ReturnType;
using type = typename scaled_permutation_result<Scalar, Lhs, Rhs, StorageIndex>::type;
};
/** \internal indices of the inverse permutation: result(indices(k)) = k. */
template <typename IndicesType, typename Result>
EIGEN_DEVICE_FUNC void invert_permutation_indices(const IndicesType& indices, Result& result) {
using StorageIndex = typename Result::Scalar;
for (Index k = 0; k < indices.size(); ++k) result.coeffRef(indices.coeff(k)) = StorageIndex(k);
}
} // namespace internal
/** \class ScaledPermutationBase
* \ingroup Core_Module
*
* \brief Base class for scaled permutation matrices
*
* A scaled permutation is the product \f$P D\f$ of a permutation matrix and a diagonal matrix: a square matrix with
* exactly one nonzero per row and per column. It is stored as the permutation's indices and one scale per
* column, so that coefficient (indices()(k), k) equals scales()(k).
*
* The set is closed under products with permutations, diagonal matrices, scalars and other scaled permutations,
* and under inversion and transposition; all of these cost \f$O(n)\f$ and return a plain ScaledPermutationMatrix.
* Products with a dense matrix scale and permute its rows or columns in \f$O(nm)\f$, and assigning to a dense matrix
* scatters the scales, so writing `P * D * x` or `P * D * P.inverse()` never forms an \f$n \times n\f$ intermediate.
* Sums with a dense matrix are lazy expressions.
*
* \sa class ScaledPermutationMatrix, class PermutationBase, class DiagonalBase
*/
template <typename Derived>
class ScaledPermutationBase : public EigenBase<Derived> {
using Traits = internal::traits<Derived>;
using Base = EigenBase<Derived>;
public:
#ifndef EIGEN_PARSED_BY_DOXYGEN
using Scalar = typename Traits::Scalar;
using RealScalar = typename NumTraits<Scalar>::Real;
using StorageIndex = typename Traits::StorageIndex;
using StorageKind = typename Traits::StorageKind;
using PermutationType = typename Traits::PermutationType;
using ScalesType = typename Traits::ScalesType;
using IndicesType = typename PermutationType::IndicesType;
static constexpr int RowsAtCompileTime = Traits::RowsAtCompileTime;
static constexpr int ColsAtCompileTime = Traits::ColsAtCompileTime;
static constexpr int MaxRowsAtCompileTime = Traits::MaxRowsAtCompileTime;
static constexpr int MaxColsAtCompileTime = Traits::MaxColsAtCompileTime;
static constexpr int SizeAtCompileTime = internal::size_at_compile_time(RowsAtCompileTime, ColsAtCompileTime);
static constexpr int MaxSizeAtCompileTime =
internal::size_at_compile_time(MaxRowsAtCompileTime, MaxColsAtCompileTime);
static constexpr bool IsVectorAtCompileTime = false;
static constexpr unsigned int Flags = Traits::Flags;
using DenseMatrixType =
Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime, 0, MaxRowsAtCompileTime, MaxColsAtCompileTime>;
using PlainObject = ScaledPermutationMatrix<Scalar, RowsAtCompileTime, MaxRowsAtCompileTime, StorageIndex>;
using Nested = const Derived&;
using Base::derived;
#endif
/** \returns the number of rows */
EIGEN_DEVICE_FUNC constexpr Index rows() const { return indices().size(); }
/** \returns the number of columns */
EIGEN_DEVICE_FUNC constexpr Index cols() const { return indices().size(); }
/** \returns the permutation factor \f$P\f$ */
EIGEN_DEVICE_FUNC const PermutationType& permutation() const { return derived().permutation(); }
/** \returns the permutation's indices: column \c k has its nonzero in row `indices()(k)` */
EIGEN_DEVICE_FUNC constexpr const IndicesType& indices() const { return derived().indices(); }
/** \returns the scales: column \c k's nonzero equals `scales()(k)` */
EIGEN_DEVICE_FUNC const ScalesType& scales() const { return derived().scales(); }
/** \returns the coefficient at (\a row, \a col) of the represented matrix */
EIGEN_DEVICE_FUNC Scalar coeff(Index row, Index col) const {
eigen_assert(row >= 0 && col >= 0 && row < rows() && col < cols());
return Index(indices().coeff(col)) == row ? scales().coeff(col) : Scalar(0);
}
/** \returns the represented matrix as a plain dense matrix */
EIGEN_DEVICE_FUNC DenseMatrixType toDenseMatrix() const { return derived(); }
#ifndef EIGEN_PARSED_BY_DOXYGEN
template <typename DenseDerived>
EIGEN_DEVICE_FUNC void evalTo(MatrixBase<DenseDerived>& other) const {
other.setZero();
for (Index k = 0; k < rows(); ++k) other.coeffRef(indices().coeff(k), k) = scales().coeff(k);
}
#endif
/** \returns the inverse, \f$(PD)^{-1} = D^{-1} P^{-1}\f$, which has \f$1 / \text{scales}(k)\f$ at
* (\c k, `indices()(k)`). The scales must be nonzero. */
PlainObject inverse() const {
PlainObject result(rows());
for (Index k = 0; k < rows(); ++k) {
const Index image = indices().coeff(k);
result.indices().coeffRef(image) = StorageIndex(k);
result.scales().coeffRef(image) = Scalar(1) / scales().coeff(k);
}
return result;
}
/** \returns the transpose, which has `scales()(k)` at (\c k, `indices()(k)`) */
PlainObject transpose() const {
PlainObject result(rows());
for (Index k = 0; k < rows(); ++k) {
const Index image = indices().coeff(k);
result.indices().coeffRef(image) = StorageIndex(k);
result.scales().coeffRef(image) = scales().coeff(k);
}
return result;
}
/** \returns the adjoint, the transpose with conjugated scales */
PlainObject adjoint() const {
PlainObject result = transpose();
result.scales() = result.scales().conjugate();
return result;
}
/** \returns the determinant, the permutation's sign times the product of the scales */
Scalar determinant() const { return Scalar(RealScalar(permutation().determinant())) * scales().prod(); }
/** \returns the dense matrix product of \c *this by the dense matrix \a other: rows of \a other are scaled and
* permuted. */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC const Product<Derived, OtherDerived, DefaultProduct> operator*(
const MatrixBase<OtherDerived>& other) const {
return Product<Derived, OtherDerived, DefaultProduct>(derived(), other.derived());
}
/** \returns the dense matrix product of the dense matrix \a other by \a scaled: columns of \a other are scaled
* and permuted. */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC friend const Product<OtherDerived, Derived, DefaultProduct> operator*(
const MatrixBase<OtherDerived>& other, const ScaledPermutationBase& scaled) {
return Product<OtherDerived, Derived, DefaultProduct>(other.derived(), scaled.derived());
}
/** \returns the dense matrix product of \c *this by the triangular or self-adjoint view \a other */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC const Product<Derived, OtherDerived, DefaultProduct> operator*(
const TriangularBase<OtherDerived>& other) const {
return Product<Derived, OtherDerived, DefaultProduct>(derived(), other.derived());
}
/** \returns the dense matrix product of the triangular or self-adjoint view \a other by \a scaled */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC friend const Product<OtherDerived, Derived, DefaultProduct> operator*(
const TriangularBase<OtherDerived>& other, const ScaledPermutationBase& scaled) {
return Product<OtherDerived, Derived, DefaultProduct>(other.derived(), scaled.derived());
}
// Closed-form products, all O(n). With S e_c = v_c e_{tau(c)} the rules are:
// S D: scale v_c d_c D S: scale d_{tau(c)} v_c
// S P: (tau(sigma(c)), v_{sigma(c)}) P S: (sigma(tau(c)), v_c)
// S T: (tau(tau2(c)), v_{tau2(c)} v2_c)
/** \returns the scaled permutation \c *this times the diagonal matrix \a other */
template <typename OtherDerived>
typename internal::scaled_permutation_product_result<Derived, OtherDerived, StorageIndex>::type operator*(
const DiagonalBase<OtherDerived>& other) const {
using Result = typename internal::scaled_permutation_product_result<Derived, OtherDerived, StorageIndex>::type;
eigen_assert(rows() == other.rows());
return Result(indices(), scales().cwiseProduct(other.diagonal()));
}
/** \returns the scaled permutation diagonal matrix \a other times \a scaled */
template <typename OtherDerived>
friend typename internal::scaled_permutation_product_result<OtherDerived, Derived, StorageIndex>::type operator*(
const DiagonalBase<OtherDerived>& other, const ScaledPermutationBase& scaled) {
using Result = typename internal::scaled_permutation_product_result<OtherDerived, Derived, StorageIndex>::type;
eigen_assert(other.rows() == scaled.rows());
Result result(scaled.rows());
result.indices() = scaled.indices();
for (Index k = 0; k < scaled.rows(); ++k)
result.scales().coeffRef(k) = other.diagonal().coeff(scaled.indices().coeff(k)) * scaled.scales().coeff(k);
return result;
}
/** \returns the scaled permutation \c *this times the permutation \a other */
template <typename OtherDerived>
PlainObject operator*(const PermutationBase<OtherDerived>& other) const {
eigen_assert(rows() == other.rows());
PlainObject result(rows());
for (Index k = 0; k < rows(); ++k) {
const Index source = other.indices().coeff(k);
result.indices().coeffRef(k) = indices().coeff(source);
result.scales().coeffRef(k) = scales().coeff(source);
}
return result;
}
/** \returns the scaled permutation permutation \a other times \a scaled */
template <typename OtherDerived>
friend PlainObject operator*(const PermutationBase<OtherDerived>& other, const ScaledPermutationBase& scaled) {
eigen_assert(other.rows() == scaled.rows());
PlainObject result(scaled.rows());
for (Index k = 0; k < scaled.rows(); ++k)
result.indices().coeffRef(k) = StorageIndex(other.indices().coeff(scaled.indices().coeff(k)));
result.scales() = scaled.scales();
return result;
}
/** \returns the scaled permutation \c *this times the inverse permutation \a other */
template <typename OtherDerived>
PlainObject operator*(const InverseImpl<OtherDerived, PermutationStorage>& other) const {
eigen_assert(rows() == other.rows());
IndicesType inverse(rows());
internal::invert_permutation_indices(other.derived().nestedExpression().indices(), inverse);
PlainObject result(rows());
for (Index k = 0; k < rows(); ++k) {
const Index source = inverse.coeff(k);
result.indices().coeffRef(k) = indices().coeff(source);
result.scales().coeffRef(k) = scales().coeff(source);
}
return result;
}
/** \returns the scaled permutation inverse permutation \a other times \a scaled */
template <typename OtherDerived>
friend PlainObject operator*(const InverseImpl<OtherDerived, PermutationStorage>& other,
const ScaledPermutationBase& scaled) {
eigen_assert(other.rows() == scaled.rows());
IndicesType inverse(scaled.rows());
internal::invert_permutation_indices(other.derived().nestedExpression().indices(), inverse);
PlainObject result(scaled.rows());
for (Index k = 0; k < scaled.rows(); ++k) result.indices().coeffRef(k) = inverse.coeff(scaled.indices().coeff(k));
result.scales() = scaled.scales();
return result;
}
/** \returns the scaled permutation \c *this times the scaled permutation \a other */
template <typename OtherDerived>
typename internal::scaled_permutation_product_result<Derived, OtherDerived, StorageIndex>::type operator*(
const ScaledPermutationBase<OtherDerived>& other) const {
using Result = typename internal::scaled_permutation_product_result<Derived, OtherDerived, StorageIndex>::type;
eigen_assert(rows() == other.rows());
Result result(rows());
for (Index k = 0; k < rows(); ++k) {
const Index source = other.indices().coeff(k);
result.indices().coeffRef(k) = indices().coeff(source);
result.scales().coeffRef(k) = scales().coeff(source) * other.scales().coeff(k);
}
return result;
}
/** \returns \c *this with its scales multiplied by \a alpha */
PlainObject operator*(const Scalar& alpha) const { return PlainObject(indices(), scales() * alpha); }
/** \returns \a scaled with its scales multiplied by \a alpha */
friend PlainObject operator*(const Scalar& alpha, const ScaledPermutationBase& scaled) {
return PlainObject(scaled.indices(), alpha * scaled.scales());
}
/** \returns \c *this with its scales negated */
PlainObject operator-() const { return PlainObject(indices(), -scales()); }
// Sums with a dense matrix are lazy: the scaled permutation is read through its index-based evaluator.
/** \returns the lazy sum of the dense matrix \a lhs and the scaled permutation \a rhs */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC friend const EIGEN_CWISE_BINARY_RETURN_TYPE(OtherDerived, Derived, internal::scalar_sum_op)
operator+(const MatrixBase<OtherDerived>& lhs, const ScaledPermutationBase & rhs) {
return EIGEN_CWISE_BINARY_RETURN_TYPE(OtherDerived, Derived, internal::scalar_sum_op)(lhs.derived(), rhs.derived());
}
/** \returns the lazy sum of the scaled permutation \a lhs and the dense matrix \a rhs */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC friend const EIGEN_CWISE_BINARY_RETURN_TYPE(Derived, OtherDerived, internal::scalar_sum_op)
operator+(const ScaledPermutationBase & lhs, const MatrixBase<OtherDerived>& rhs) {
return EIGEN_CWISE_BINARY_RETURN_TYPE(Derived, OtherDerived, internal::scalar_sum_op)(lhs.derived(), rhs.derived());
}
/** \returns the lazy difference of the dense matrix \a lhs and the scaled permutation \a rhs */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC friend const EIGEN_CWISE_BINARY_RETURN_TYPE(OtherDerived, Derived, internal::scalar_difference_op)
operator-(const MatrixBase<OtherDerived>& lhs, const ScaledPermutationBase & rhs) {
return EIGEN_CWISE_BINARY_RETURN_TYPE(OtherDerived, Derived, internal::scalar_difference_op)(lhs.derived(),
rhs.derived());
}
/** \returns the lazy difference of the scaled permutation \a lhs and the dense matrix \a rhs */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC friend const EIGEN_CWISE_BINARY_RETURN_TYPE(Derived, OtherDerived, internal::scalar_difference_op)
operator-(const ScaledPermutationBase & lhs, const MatrixBase<OtherDerived>& rhs) {
return EIGEN_CWISE_BINARY_RETURN_TYPE(Derived, OtherDerived, internal::scalar_difference_op)(lhs.derived(),
rhs.derived());
}
};
/** \class ScaledPermutationMatrix
* \ingroup Core_Module
*
* \brief Scaled permutation matrix \f$P D\f$ with its storage
*
* \tparam Scalar_ the type of the scales
* \tparam SizeAtCompileTime the number of rows/cols, or Dynamic
* \tparam MaxSizeAtCompileTime the maximum number of rows/cols, or Dynamic; defaults to SizeAtCompileTime
* \tparam StorageIndex_ the integer type of the permutation's indices
*
* This is the type returned by the products of a permutation (or its inverse) and a diagonal matrix, in either
* order. See ScaledPermutationBase for the operations.
*
* \sa class ScaledPermutationBase, class PermutationMatrix, class DiagonalMatrix
*/
template <typename Scalar_, int SizeAtCompileTime, int MaxSizeAtCompileTime, typename StorageIndex_>
class ScaledPermutationMatrix
: public ScaledPermutationBase<
ScaledPermutationMatrix<Scalar_, SizeAtCompileTime, MaxSizeAtCompileTime, StorageIndex_>> {
using Base = ScaledPermutationBase<ScaledPermutationMatrix>;
using Traits = internal::traits<ScaledPermutationMatrix>;
public:
#ifndef EIGEN_PARSED_BY_DOXYGEN
using Scalar = Scalar_;
using StorageIndex = StorageIndex_;
using PermutationType = typename Traits::PermutationType;
using ScalesType = typename Traits::ScalesType;
using IndicesType = typename PermutationType::IndicesType;
#endif
/** Default constructor; the matrix is uninitialized. */
EIGEN_DEVICE_FUNC ScaledPermutationMatrix() = default;
/** Constructs an uninitialized scaled permutation of the given size. */
EIGEN_DEVICE_FUNC explicit ScaledPermutationMatrix(Index size) : m_permutation(size), m_scales(size) {}
/** Constructs the product \a permutation times \a diagonal. */
template <typename PermutationDerived, typename DiagonalDerived>
EIGEN_DEVICE_FUNC ScaledPermutationMatrix(const PermutationBase<PermutationDerived>& permutation,
const DiagonalBase<DiagonalDerived>& diagonal)
: m_permutation(permutation), m_scales(diagonal.diagonal()) {
eigen_assert(permutation.rows() == diagonal.rows());
}
/** Constructs the scaled permutation with the given \a indices and \a scales. */
template <typename IndicesDerived, typename ScalesDerived>
EIGEN_DEVICE_FUNC ScaledPermutationMatrix(const MatrixBase<IndicesDerived>& indices,
const MatrixBase<ScalesDerived>& scales)
: m_permutation(indices), m_scales(scales) {
eigen_assert(indices.size() == scales.size());
}
/** Converts the permutation \a permutation, all scales being one. */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC explicit ScaledPermutationMatrix(const PermutationBase<OtherDerived>& permutation)
: m_permutation(permutation), m_scales(ScalesType::Ones(permutation.rows())) {}
/** Converts the diagonal matrix \a diagonal, the permutation being the identity. */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC explicit ScaledPermutationMatrix(const DiagonalBase<OtherDerived>& diagonal)
: m_permutation(diagonal.rows()), m_scales(diagonal.diagonal()) {
m_permutation.setIdentity();
}
/** Copies the scaled permutation \a other. */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC ScaledPermutationMatrix(const ScaledPermutationBase<OtherDerived>& other)
: m_permutation(other.permutation()), m_scales(other.scales()) {}
/** Copies the scaled permutation \a other. */
template <typename OtherDerived>
EIGEN_DEVICE_FUNC ScaledPermutationMatrix& operator=(const ScaledPermutationBase<OtherDerived>& other) {
m_permutation = other.permutation();
m_scales = other.scales();
return *this;
}
/** \returns the permutation factor */
EIGEN_DEVICE_FUNC const PermutationType& permutation() const { return m_permutation; }
/** \returns a reference to the permutation factor */
EIGEN_DEVICE_FUNC PermutationType& permutation() { return m_permutation; }
/** \returns the permutation's indices */
EIGEN_DEVICE_FUNC constexpr const IndicesType& indices() const { return m_permutation.indices(); }
/** \returns a reference to the permutation's indices */
EIGEN_DEVICE_FUNC constexpr IndicesType& indices() { return m_permutation.indices(); }
/** \returns the scales */
EIGEN_DEVICE_FUNC const ScalesType& scales() const { return m_scales; }
/** \returns a reference to the scales */
EIGEN_DEVICE_FUNC ScalesType& scales() { return m_scales; }
/** Resizes to the given size, leaving the coefficients uninitialized. */
EIGEN_DEVICE_FUNC void resize(Index size) {
m_permutation.resize(size);
m_scales.resize(size);
}
/** Sets \c *this to the identity matrix. */
EIGEN_DEVICE_FUNC void setIdentity() {
m_permutation.setIdentity();
m_scales.setOnes();
}
/** Sets \c *this to the identity matrix of the given size. */
EIGEN_DEVICE_FUNC void setIdentity(Index size) {
resize(size);
setIdentity();
}
protected:
PermutationType m_permutation;
ScalesType m_scales;
};
// The products of a permutation (or its inverse) and a diagonal matrix. With P e_c = e_{sigma(c)}:
// P D: (sigma(c), d_c) D P: (sigma(c), d_{sigma(c)})
// P^-1 D: (sigma^-1(c), d_c) D P^-1: (sigma^-1(c), d_{sigma^-1(c)})
/** \returns the scaled permutation \a permutation times \a diagonal
* \relates ScaledPermutationMatrix */
template <typename PermutationDerived, typename DiagonalDerived>
typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationDerived, DiagonalDerived,
typename PermutationDerived::StorageIndex>::type
operator*(const PermutationBase<PermutationDerived>& permutation, const DiagonalBase<DiagonalDerived>& diagonal) {
using Result =
typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationDerived,
DiagonalDerived, typename PermutationDerived::StorageIndex>::type;
eigen_assert(permutation.rows() == diagonal.rows());
return Result(permutation.indices(), diagonal.diagonal());
}
/** \returns the scaled permutation \a diagonal times \a permutation
* \relates ScaledPermutationMatrix */
template <typename DiagonalDerived, typename PermutationDerived>
typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationDerived, DiagonalDerived,
typename PermutationDerived::StorageIndex>::type
operator*(const DiagonalBase<DiagonalDerived>& diagonal, const PermutationBase<PermutationDerived>& permutation) {
using Result =
typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationDerived,
DiagonalDerived, typename PermutationDerived::StorageIndex>::type;
eigen_assert(permutation.rows() == diagonal.rows());
Result result(permutation.rows());
result.indices() = permutation.indices();
for (Index k = 0; k < permutation.rows(); ++k)
result.scales().coeffRef(k) = diagonal.diagonal().coeff(permutation.indices().coeff(k));
return result;
}
/** \returns the scaled permutation \a inverse (an inverse permutation) times \a diagonal
* \relates ScaledPermutationMatrix */
template <typename PermutationType, typename DiagonalDerived>
typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationType, DiagonalDerived,
typename PermutationType::StorageIndex>::type
operator*(const InverseImpl<PermutationType, PermutationStorage>& inverse,
const DiagonalBase<DiagonalDerived>& diagonal) {
using Result =
typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationType, DiagonalDerived,
typename PermutationType::StorageIndex>::type;
eigen_assert(inverse.rows() == diagonal.rows());
Result result(inverse.rows());
internal::invert_permutation_indices(inverse.derived().nestedExpression().indices(), result.indices());
result.scales() = diagonal.diagonal();
return result;
}
/** \returns the scaled permutation \a diagonal times \a inverse (an inverse permutation)
* \relates ScaledPermutationMatrix */
template <typename DiagonalDerived, typename PermutationType>
typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationType, DiagonalDerived,
typename PermutationType::StorageIndex>::type
operator*(const DiagonalBase<DiagonalDerived>& diagonal,
const InverseImpl<PermutationType, PermutationStorage>& inverse) {
using Result =
typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationType, DiagonalDerived,
typename PermutationType::StorageIndex>::type;
eigen_assert(inverse.rows() == diagonal.rows());
Result result(inverse.rows());
internal::invert_permutation_indices(inverse.derived().nestedExpression().indices(), result.indices());
for (Index k = 0; k < inverse.rows(); ++k)
result.scales().coeffRef(k) = diagonal.diagonal().coeff(result.indices().coeff(k));
return result;
}
namespace internal {
template <typename Scalar_, int SizeAtCompileTime, int MaxSizeAtCompileTime, typename StorageIndex_>
struct evaluator_traits<ScaledPermutationMatrix<Scalar_, SizeAtCompileTime, MaxSizeAtCompileTime, StorageIndex_>> {
using Kind = IndexBased;
using Shape = ScaledPermutationShape;
};
/** \internal Index-based evaluator, for the lazy sums with a dense matrix. Every coefficient is synthesized, so there
* is no linear, packet or direct access. */
template <typename Scalar_, int SizeAtCompileTime, int MaxSizeAtCompileTime, typename StorageIndex_>
struct evaluator<ScaledPermutationMatrix<Scalar_, SizeAtCompileTime, MaxSizeAtCompileTime, StorageIndex_>>
: evaluator_base<ScaledPermutationMatrix<Scalar_, SizeAtCompileTime, MaxSizeAtCompileTime, StorageIndex_>> {
using XprType = ScaledPermutationMatrix<Scalar_, SizeAtCompileTime, MaxSizeAtCompileTime, StorageIndex_>;
using Scalar = Scalar_;
using CoeffReturnType = Scalar;
static constexpr int CoeffReadCost =
int(NumTraits<StorageIndex_>::ReadCost) + int(NumTraits<Scalar>::ReadCost) + int(NumTraits<Scalar>::AddCost);
static constexpr unsigned int Flags = 0;
static constexpr int Alignment = 0;
EIGEN_DEVICE_FUNC explicit evaluator(const XprType& xpr) : m_indices(xpr.indices()), m_scales(xpr.scales()) {}
EIGEN_DEVICE_FUNC Scalar coeff(Index row, Index col) const {
return Index(m_indices.coeff(col)) == row ? m_scales.coeff(col) : Scalar(0);
}
// Linear access is requested only for vector-shaped operands (inner products), i.e. a 1x1 matrix.
EIGEN_DEVICE_FUNC Scalar coeff(Index index) const {
eigen_assert(index == 0);
return m_scales.coeff(index);
}
protected:
evaluator<typename XprType::IndicesType> m_indices;
evaluator<typename XprType::ScalesType> m_scales;
};
struct ScaledPermutation2Dense {};
template <>
struct AssignmentKind<DenseShape, ScaledPermutationShape> {
using Kind = ScaledPermutation2Dense;
};
// Scaled permutation to dense assignment: zero fill plus one scatter of n coefficients.
template <typename DstXprType, typename SrcXprType, typename Functor>
struct Assignment<DstXprType, SrcXprType, Functor, ScaledPermutation2Dense> {
static EIGEN_DEVICE_FUNC void run(
DstXprType& dst, const SrcXprType& src,
const internal::assign_op<typename DstXprType::Scalar, typename SrcXprType::Scalar>&) {
if (dst.rows() != src.rows() || dst.cols() != src.cols()) dst.resize(src.rows(), src.cols());
dst.setZero();
for (Index k = 0; k < src.rows(); ++k) dst.coeffRef(src.indices().coeff(k), k) = src.scales().coeff(k);
}
static EIGEN_DEVICE_FUNC void run(
DstXprType& dst, const SrcXprType& src,
const internal::add_assign_op<typename DstXprType::Scalar, typename SrcXprType::Scalar>&) {
eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
for (Index k = 0; k < src.rows(); ++k) dst.coeffRef(src.indices().coeff(k), k) += src.scales().coeff(k);
}
static EIGEN_DEVICE_FUNC void run(
DstXprType& dst, const SrcXprType& src,
const internal::sub_assign_op<typename DstXprType::Scalar, typename SrcXprType::Scalar>&) {
eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
for (Index k = 0; k < src.rows(); ++k) dst.coeffRef(src.indices().coeff(k), k) -= src.scales().coeff(k);
}
};
/***************************************************************************
* Products with a dense matrix: rows (S * X) or columns (X * S) are scaled and permuted, O(n m).
* evalTo reuses the dense permutation kernel, which handles X aliasing dst, and scales in place afterwards;
* the accumulating form works on the nested (evaluated once) operand.
* scaleAndAddTo evaluates dst += alpha * (lhs * rhs) with alpha a left factor of the product. Scalar
* multiplication need not commute, so both kernels keep that operand order: S * X regroups it as
* (alpha * v_c) * X(c, j) by associativity; X * S keeps alpha * (X(i, tau(c)) * v_c), as folding alpha into
* v_c would move it past the dense coefficient.
***************************************************************************/
template <typename Lhs, typename Rhs, int ProductTag>
struct generic_product_impl<Lhs, Rhs, ScaledPermutationShape, DenseShape, ProductTag>
: generic_product_impl_base<Lhs, Rhs,
generic_product_impl<Lhs, Rhs, ScaledPermutationShape, DenseShape, ProductTag>> {
using Scalar = typename Product<Lhs, Rhs>::Scalar;
template <typename Dest>
static EIGEN_DEVICE_FUNC void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
permutation_matrix_product<Rhs, OnTheLeft, false, DenseShape>::run(dst, lhs.permutation(), rhs);
for (Index k = 0; k < lhs.rows(); ++k) {
auto row = dst.row(lhs.indices().coeff(k));
row = lhs.scales().coeff(k) * row;
}
}
template <typename Dest>
static EIGEN_DEVICE_FUNC void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
typename nested_eval<Rhs, 1>::type rhsNested(rhs);
for (Index k = 0; k < lhs.rows(); ++k)
dst.row(lhs.indices().coeff(k)) += (alpha * lhs.scales().coeff(k)) * rhsNested.row(k);
}
};
template <typename Lhs, typename Rhs, int ProductTag>
struct generic_product_impl<Lhs, Rhs, DenseShape, ScaledPermutationShape, ProductTag>
: generic_product_impl_base<Lhs, Rhs,
generic_product_impl<Lhs, Rhs, DenseShape, ScaledPermutationShape, ProductTag>> {
using Scalar = typename Product<Lhs, Rhs>::Scalar;
template <typename Dest>
static EIGEN_DEVICE_FUNC void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
permutation_matrix_product<Lhs, OnTheRight, false, DenseShape>::run(dst, rhs.permutation(), lhs);
for (Index k = 0; k < rhs.rows(); ++k) dst.col(k) *= rhs.scales().coeff(k);
}
template <typename Dest>
static EIGEN_DEVICE_FUNC void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
typename nested_eval<Lhs, 1>::type lhsNested(lhs);
for (Index k = 0; k < rhs.rows(); ++k)
dst.col(k) += alpha * (lhsNested.col(rhs.indices().coeff(k)) * rhs.scales().coeff(k));
}
};
// A triangular or self-adjoint operand is evaluated into a plain matrix first, as toDenseMatrix() does.
template <typename Lhs, typename Rhs, int ProductTag, bool ViewOnTheRight>
struct scaled_permutation_view_product_impl;
template <typename Lhs, typename Rhs, int ProductTag>
struct scaled_permutation_view_product_impl<Lhs, Rhs, ProductTag, true>
: generic_product_impl_base<Lhs, Rhs, scaled_permutation_view_product_impl<Lhs, Rhs, ProductTag, true>> {
using Scalar = typename Product<Lhs, Rhs>::Scalar;
using DenseType = typename Rhs::DenseMatrixType;
using DenseImpl = generic_product_impl<Lhs, DenseType, ScaledPermutationShape, DenseShape, ProductTag>;
template <typename Dest>
static EIGEN_DEVICE_FUNC void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
const DenseType dense(rhs);
DenseImpl::evalTo(dst, lhs, dense);
}
template <typename Dest>
static EIGEN_DEVICE_FUNC void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
const DenseType dense(rhs);
DenseImpl::scaleAndAddTo(dst, lhs, dense, alpha);
}
};
template <typename Lhs, typename Rhs, int ProductTag>
struct scaled_permutation_view_product_impl<Lhs, Rhs, ProductTag, false>
: generic_product_impl_base<Lhs, Rhs, scaled_permutation_view_product_impl<Lhs, Rhs, ProductTag, false>> {
using Scalar = typename Product<Lhs, Rhs>::Scalar;
using DenseType = typename Lhs::DenseMatrixType;
using DenseImpl = generic_product_impl<DenseType, Rhs, DenseShape, ScaledPermutationShape, ProductTag>;
template <typename Dest>
static EIGEN_DEVICE_FUNC void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
const DenseType dense(lhs);
DenseImpl::evalTo(dst, dense, rhs);
}
template <typename Dest>
static EIGEN_DEVICE_FUNC void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
const DenseType dense(lhs);
DenseImpl::scaleAndAddTo(dst, dense, rhs, alpha);
}
};
template <typename Lhs, typename Rhs, int ProductTag>
struct generic_product_impl<Lhs, Rhs, ScaledPermutationShape, TriangularShape, ProductTag>
: scaled_permutation_view_product_impl<Lhs, Rhs, ProductTag, true> {};
template <typename Lhs, typename Rhs, int ProductTag>
struct generic_product_impl<Lhs, Rhs, ScaledPermutationShape, SelfAdjointShape, ProductTag>
: scaled_permutation_view_product_impl<Lhs, Rhs, ProductTag, true> {};
template <typename Lhs, typename Rhs, int ProductTag>
struct generic_product_impl<Lhs, Rhs, TriangularShape, ScaledPermutationShape, ProductTag>
: scaled_permutation_view_product_impl<Lhs, Rhs, ProductTag, false> {};
template <typename Lhs, typename Rhs, int ProductTag>
struct generic_product_impl<Lhs, Rhs, SelfAdjointShape, ScaledPermutationShape, ProductTag>
: scaled_permutation_view_product_impl<Lhs, Rhs, ProductTag, false> {};
} // namespace internal
} // namespace Eigen
#endif // EIGEN_SCALEDPERMUTATIONMATRIX_H