|  | // 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 |