A MATLAB function to generate random symmetric matrix

3 minute read

Published:

The MATLAB builtin function gallery('randsvd',...) can only generate a random matrix or a symmetric positive definite matrix. However, sometimes, we need to test our algorithms for symmetric matrices. Although you can get a symmetric matrix by A = randn(n); A = A + A';, it would be nice to inherit the features from gallery('randsvd'), such as the ability to control the distributions of the singular values. The attached code achieves such desire by

  • first, generate singular values,
  • then construct a random orthogonal matrix using qmult, and
  • finally multiply the orthogonal matrix at both sides of the diagonal matrix that contains these singular values.

We modified the source code of randsvd, and using two additional functions: qmult and mysign, which are all from Prof. Nicholas J. Higham.

  • Version: Thursday December 6, 2023
  • Acknowledgement: Prof. Nicholas J. Higham
function A = my_randsvd(n, kappa, mode)
%MY_RANDSVD Customized gallery('randsvd')
%  A = MY_RANDSVD(N, KAPPA, MODE) generates
%  a random symmetric matrix A of order N with 
%  cond(A) = KAPPA and singular values from distribution MODE.
%
% Input :
%      N : Size of the matrix.
%
%  KAPPA : pre-defined 2-norm condition number for the output,
%          if kappa is negative, it will return a symmetric
%          positive definite matrix.
%		   Default value is 100.
% https://www.mathworks.com/help/matlab/ref/gallery.html 
%  
%  
%   MODE : one of the following values:
%	       1: one large singular value,
%		   2: one small singular value,
%		   3: geometrically distributed singular values,
%		   4: arithmetically distributed singular values,
%		   5: random singular values with uniformly distributed
%		      logarithm.
%          Default value is 3. 
%
% Reference:
% The MATLAB 'gallery' function.
classname = 'double';

switch nargin
	case 1
		kappa = 100;
		mode = 3;
	case 2
		mode = 3;
end

% check if we need export p.d. matrix
if sign(kappa) == 1, pd = false; else, pd = true; end
kappa = abs(kappa);

switch mode % Set up vector of singular values
	case 1
		sigma = ones(n,1)./kappa;
		sigma(1) = 1;

	case 2
		sigma = ones(n,1);
		sigma(n) = 1/kappa;

	case 3
		factor = kappa^(-1/(n-1));
		sigma = factor.^(0:n-1);

	case 4
		sigma = ones(n,1) - (0:n-1)'/(n-1)*(1-1/kappa);

	case 5    % In this case cond(A) <= kappa.
		sigma = exp( -rand(n,1)*log(kappa) );

	otherwise
		error(message('MATLAB:randsvd:invalidMode'));
end

sigma = cast(sigma,classname);

if ~pd % randomly introducing signs
	[l,j] = size(sigma(2:n-1));
	signs = sign(rand(1,n-2)*2-1)';
	signs = reshape(signs,[l,j]);
	sigma(2:n-1) = sigma(2:n-1) .* signs;
end

Q = qmult(n,1,classname);
A = Q*diag(sigma)*Q';
A = (A + A')/2; % ensure symmetry

end

Here is the code for qmult and mysign:

function B = qmult(A,method,classname)
%QMULT Pre-multiply matrix by random orthogonal matrix.
%   QMULT(A) returns Q*A where Q is a random real orthogonal matrix
%   from the Haar distribution of dimension the number of rows in A.
%   Special case: if A is a scalar then QMULT(A) is the same as QMULT(EYE(A)).
%   QMULT(A,METHOD) specifies how the computations are carried out.
%   METHOD = 0 is the default, while METHOD = 1 uses a call to QR,
%   which is much faster for large dimensions, even though it uses more flops.

%   Called by RANDCOLU, RANDCORR, RANDJORTH, RANDSVD.

%   Reference:
%   G. W. Stewart, The efficient generation of random
%   orthogonal matrices with an application to condition estimators,
%   SIAM J. Numer. Anal., 17 (1980), 403-409.
%
%   Nicholas J. Higham
%   Copyright 1984-2020 The MathWorks, Inc.

if nargin < 2 || isempty(method)
	method = 0;
end
%  Handle scalar A.
if isscalar(A)
	n = A;
	A = eye(n,classname);
else
	n = size(A, 1);
end
if isempty(A) % nothing to do.
	B = A;
	return
end
if method == 1
	[Q,R] = qr(randn(n));
	B = Q*diag(sign(diag(R)))*A;
	return
end
d = zeros(n,1,classname);
for k = n-1:-1:1
	% Generate random Householder transformation.
	x = randn(n-k+1,1);
	s = norm(x);
	sgn = mysign(x(1));
	s = sgn*s;
	d(k) = -sgn;
	x(1) = x(1) + s;
	beta = s*x(1);
	% Apply the transformation to A.
	y = x'*A(k:n,:);
	A(k:n,:) = A(k:n,:) - x*(y/beta);
end
% Tidy up signs.
for i=1:n-1
	A(i,:) = d(i)*A(i,:);
end
A(n,:) = A(n,:)*mysign(randn);
B = A;
end
function S = mysign(A)
%MYSIGN True sign function with MYSIGN(0) = 1.

%   Called by various matrices in elmat/private.
%
%   Nicholas J. Higham
%   Copyright 1984-2013 The MathWorks, Inc.
S = sign(A);
S(S==0) = 1;
end