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
 | 
