243 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			243 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | // 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
 |