| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> |
| // Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> |
| // Copyright (C) 2021 Kolja Brix <kolja.brix@rwth-aachen.de> |
| // |
| // 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/. |
| |
| #ifndef EIGEN_RANDOM_MATRIX_HELPER |
| #define EIGEN_RANDOM_MATRIX_HELPER |
| |
| #include <typeinfo> |
| #include <Eigen/QR> // required for createRandomPIMatrixOfRank and generateRandomMatrixSvs |
| |
| // Forward declarations to avoid ICC warnings |
| #if EIGEN_COMP_ICC |
| |
| namespace Eigen { |
| |
| template <typename MatrixType> |
| void createRandomPIMatrixOfRank(Index desired_rank, Index rows, Index cols, MatrixType& m); |
| |
| template <typename PermutationVectorType> |
| void randomPermutationVector(PermutationVectorType& v, Index size); |
| |
| template <typename MatrixType> |
| MatrixType generateRandomUnitaryMatrix(const Index dim); |
| |
| template <typename MatrixType, typename RealScalarVectorType> |
| void generateRandomMatrixSvs(const RealScalarVectorType& svs, const Index rows, const Index cols, MatrixType& M); |
| |
| template <typename VectorType, typename RealScalar> |
| VectorType setupRandomSvs(const Index dim, const RealScalar max); |
| |
| template <typename VectorType, typename RealScalar> |
| VectorType setupRangeSvs(const Index dim, const RealScalar min, const RealScalar max); |
| |
| } // end namespace Eigen |
| |
| #endif // EIGEN_COMP_ICC |
| |
| namespace Eigen { |
| |
| /** |
| * Creates a random partial isometry matrix of given rank. |
| * |
| * A partial isometry is a matrix all of whose singular values are either 0 or 1. |
| * This is very useful to test rank-revealing algorithms. |
| * |
| * @tparam MatrixType type of random partial isometry matrix |
| * @param desired_rank rank requested for the random partial isometry matrix |
| * @param rows row dimension of requested random partial isometry matrix |
| * @param cols column dimension of requested random partial isometry matrix |
| * @param m random partial isometry matrix |
| */ |
| template <typename MatrixType> |
| void createRandomPIMatrixOfRank(Index desired_rank, Index rows, Index cols, MatrixType& m) { |
| typedef typename internal::traits<MatrixType>::Scalar Scalar; |
| enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime }; |
| |
| typedef Matrix<Scalar, Dynamic, 1> VectorType; |
| typedef Matrix<Scalar, Rows, Rows> MatrixAType; |
| typedef Matrix<Scalar, Cols, Cols> MatrixBType; |
| |
| if (desired_rank == 0) { |
| m.setZero(rows, cols); |
| return; |
| } |
| |
| if (desired_rank == 1) { |
| // here we normalize the vectors to get a partial isometry |
| m = VectorType::Random(rows).normalized() * VectorType::Random(cols).normalized().transpose(); |
| return; |
| } |
| |
| MatrixAType a = MatrixAType::Random(rows, rows); |
| MatrixType d = MatrixType::Identity(rows, cols); |
| MatrixBType b = MatrixBType::Random(cols, cols); |
| |
| // set the diagonal such that only desired_rank non-zero entries remain |
| const Index diag_size = (std::min)(d.rows(), d.cols()); |
| if (diag_size != desired_rank) |
| d.diagonal().segment(desired_rank, diag_size - desired_rank) = VectorType::Zero(diag_size - desired_rank); |
| |
| HouseholderQR<MatrixAType> qra(a); |
| HouseholderQR<MatrixBType> qrb(b); |
| m = qra.householderQ() * d * qrb.householderQ(); |
| } |
| |
| /** |
| * Generate random permutation vector. |
| * |
| * @tparam PermutationVectorType type of vector used to store permutation |
| * @param v permutation vector |
| * @param size length of permutation vector |
| */ |
| template <typename PermutationVectorType> |
| void randomPermutationVector(PermutationVectorType& v, Index size) { |
| typedef typename PermutationVectorType::Scalar Scalar; |
| v.resize(size); |
| for (Index i = 0; i < size; ++i) v(i) = Scalar(i); |
| if (size == 1) return; |
| for (Index n = 0; n < 3 * size; ++n) { |
| Index i = internal::random<Index>(0, size - 1); |
| Index j; |
| do j = internal::random<Index>(0, size - 1); |
| while (j == i); |
| std::swap(v(i), v(j)); |
| } |
| } |
| |
| /** |
| * Generate a random unitary matrix of prescribed dimension. |
| * |
| * The algorithm is using a random Householder sequence to produce |
| * a random unitary matrix. |
| * |
| * @tparam MatrixType type of matrix to generate |
| * @param dim row and column dimension of the requested square matrix |
| * @return random unitary matrix |
| */ |
| template <typename MatrixType> |
| MatrixType generateRandomUnitaryMatrix(const Index dim) { |
| typedef typename internal::traits<MatrixType>::Scalar Scalar; |
| typedef Matrix<Scalar, Dynamic, 1> VectorType; |
| |
| MatrixType v = MatrixType::Identity(dim, dim); |
| VectorType h = VectorType::Zero(dim); |
| for (Index i = 0; i < dim; ++i) { |
| v.col(i).tail(dim - i - 1) = VectorType::Random(dim - i - 1); |
| h(i) = 2 / v.col(i).tail(dim - i).squaredNorm(); |
| } |
| |
| const Eigen::HouseholderSequence<MatrixType, VectorType> HSeq(v, h); |
| return MatrixType(HSeq); |
| } |
| |
| /** |
| * Generation of random matrix with prescribed singular values. |
| * |
| * We generate random matrices with given singular values by setting up |
| * a singular value decomposition. By choosing the number of zeros as |
| * singular values we can specify the rank of the matrix. |
| * Moreover, we also control its spectral norm, which is the largest |
| * singular value, as well as its condition number with respect to the |
| * l2-norm, which is the quotient of the largest and smallest singular |
| * value. |
| * |
| * Reference: For details on the method see e.g. Section 8.1 (pp. 62 f) in |
| * |
| * C. C. Paige, M. A. Saunders, |
| * LSQR: An algorithm for sparse linear equations and sparse least squares. |
| * ACM Transactions on Mathematical Software 8(1), pp. 43-71, 1982. |
| * https://web.stanford.edu/group/SOL/software/lsqr/lsqr-toms82a.pdf |
| * |
| * and also the LSQR webpage https://web.stanford.edu/group/SOL/software/lsqr/. |
| * |
| * @tparam MatrixType matrix type to generate |
| * @tparam RealScalarVectorType vector type with real entries used for singular values |
| * @param svs vector of desired singular values |
| * @param rows row dimension of requested random matrix |
| * @param cols column dimension of requested random matrix |
| * @param M generated matrix with prescribed singular values |
| */ |
| template <typename MatrixType, typename RealScalarVectorType> |
| void generateRandomMatrixSvs(const RealScalarVectorType& svs, const Index rows, const Index cols, MatrixType& M) { |
| enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime }; |
| typedef typename internal::traits<MatrixType>::Scalar Scalar; |
| typedef Matrix<Scalar, Rows, Rows> MatrixAType; |
| typedef Matrix<Scalar, Cols, Cols> MatrixBType; |
| |
| const Index min_dim = (std::min)(rows, cols); |
| |
| const MatrixAType U = generateRandomUnitaryMatrix<MatrixAType>(rows); |
| const MatrixBType V = generateRandomUnitaryMatrix<MatrixBType>(cols); |
| |
| M = U.block(0, 0, rows, min_dim) * svs.asDiagonal() * V.block(0, 0, cols, min_dim).transpose(); |
| } |
| |
| /** |
| * Setup a vector of random singular values with prescribed upper limit. |
| * For use with generateRandomMatrixSvs(). |
| * |
| * Singular values are non-negative real values. By convention (to be consistent with |
| * singular value decomposition) we sort them in decreasing order. |
| * |
| * This strategy produces random singular values in the range [0, max], in particular |
| * the singular values can be zero or arbitrarily close to zero. |
| * |
| * @tparam VectorType vector type with real entries used for singular values |
| * @tparam RealScalar data type used for real entry |
| * @param dim number of singular values to generate |
| * @param max upper bound for singular values |
| * @return vector of singular values |
| */ |
| template <typename VectorType, typename RealScalar> |
| VectorType setupRandomSvs(const Index dim, const RealScalar max) { |
| VectorType svs = max / RealScalar(2) * (VectorType::Random(dim) + VectorType::Ones(dim)); |
| std::sort(svs.begin(), svs.end(), std::greater<RealScalar>()); |
| return svs; |
| } |
| |
| /** |
| * Setup a vector of random singular values with prescribed range. |
| * For use with generateRandomMatrixSvs(). |
| * |
| * Singular values are non-negative real values. By convention (to be consistent with |
| * singular value decomposition) we sort them in decreasing order. |
| * |
| * For dim > 1 this strategy generates a vector with largest entry max, smallest entry |
| * min, and remaining entries in the range [min, max]. For dim == 1 the only entry is |
| * min. |
| * |
| * @tparam VectorType vector type with real entries used for singular values |
| * @tparam RealScalar data type used for real entry |
| * @param dim number of singular values to generate |
| * @param min smallest singular value to use |
| * @param max largest singular value to use |
| * @return vector of singular values |
| */ |
| template <typename VectorType, typename RealScalar> |
| VectorType setupRangeSvs(const Index dim, const RealScalar min, const RealScalar max) { |
| VectorType svs = VectorType::Random(dim); |
| if (dim == 0) return svs; |
| if (dim == 1) { |
| svs(0) = min; |
| return svs; |
| } |
| std::sort(svs.begin(), svs.end(), std::greater<RealScalar>()); |
| |
| // scale to range [min, max] |
| const RealScalar c_min = svs(dim - 1), c_max = svs(0); |
| svs = (svs - VectorType::Constant(dim, c_min)) / (c_max - c_min); |
| return min * (VectorType::Ones(dim) - svs) + max * svs; |
| } |
| |
| } // end namespace Eigen |
| |
| #endif // EIGEN_RANDOM_MATRIX_HELPER |