Skip to content

Commit 86f8d54

Browse files
committed
feat(matrices): add randsvd
1 parent ea576da commit 86f8d54

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed

Diff for: src/matrices/index.jl

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export
3737
Prolate,
3838
Randcorr,
3939
Rando,
40+
RandSVD,
4041
Toeplitz,
4142
Triw
4243

@@ -75,6 +76,7 @@ include("poisson.jl")
7576
include("prolate.jl")
7677
include("randcorr.jl")
7778
include("rando.jl")
79+
include("randsvd.jl")
7880
include("toeplitz.jl")
7981
include("triw.jl")
8082

@@ -118,6 +120,7 @@ MATRIX_GROUPS[GROUP_BUILTIN] = Set([
118120
Prolate,
119121
Randcorr,
120122
Rando,
123+
RandSVD,
121124
Toeplitz,
122125
Triw,
123126
])

Diff for: src/matrices/randsvd.jl

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""
2+
Pre-multiply by random orthogonal matrix
3+
"""
4+
function qmult!(A::Matrix{T}) where {T}
5+
n, m = size(A)
6+
7+
d = zeros(T, n)
8+
for k = n-1:-1:1
9+
10+
# generate random Householder transformation
11+
x = randn(n - k + 1)
12+
s = norm(x)
13+
sgn = sign(x[1]) + (x[1] == 0)
14+
s = sgn * s
15+
d[k] = -sgn
16+
x[1] = x[1] + s
17+
beta = s * x[1]
18+
19+
# apply the transformation to A
20+
y = x' * A[k:n, :]
21+
A[k:n, :] = A[k:n, :] - x * (y / beta)
22+
end
23+
24+
# tidy up signs
25+
for i = 1:n-1
26+
A[i, :] = d[i] * A[i, :]
27+
end
28+
A[n, :] = A[n, :] * sign(randn())
29+
return A
30+
end
31+
32+
"""
33+
Random Matrix with Pre-assigned Singular Values
34+
===============================================
35+
*Input options:*
36+
37+
+ row_dim, col_dim, kappa, mode: `row_dim` and `col_dim`
38+
are the row and column dimensions.
39+
`kappa` is the condition number of the matrix.
40+
`mode = 1`: one large singular value.
41+
`mode = 2`: one small singular value.
42+
`mode = 3`: geometrically distributed singular values.
43+
`mode = 4`: arithmetrically distributed singular values.
44+
`mode = 5`: random singular values with unif. dist. logarithm.
45+
46+
+ dim, kappa, mode: `row_dim = col_dim = dim`.
47+
48+
+ dim, kappa: `mode = 3`.
49+
50+
+ dim: `kappa = sqrt(1/eps())`, `mode = 3`.
51+
52+
*References:*
53+
54+
**N. J. Higham**, Accuracy and Stability of Numerical
55+
Algorithms, second edition, Society for Industrial and Applied Mathematics,
56+
Philadelphia, PA, USA, 2002; sec. 28.3.
57+
"""
58+
struct RandSVD{T<:Number} <: AbstractMatrix{T}
59+
m::Integer
60+
n::Integer
61+
kappa::T
62+
M::AbstractMatrix{T}
63+
64+
function RandSVD{T}(m::Integer, n::Integer, kappa::T, mode::Integer) where {T<:Number}
65+
m >= 0 || throw(ArgumentError("$m < 0"))
66+
n >= 0 || throw(ArgumentError("$n < 0"))
67+
kappa >= 1 || throw(ArgumentError("Condition number must be at least 1."))
68+
mode 1:5 || throw(ArgumentError("mode not in 1:5"))
69+
70+
# create matrix
71+
p = min(m, n)
72+
if p == 1 # handle 1-d case
73+
return ones(T, 1, 1) * kappa
74+
end
75+
76+
if mode == 1
77+
sigma = ones(p) ./ kappa
78+
sigma[1] = one(T)
79+
elseif mode == 2
80+
sigma = ones(T, p)
81+
sigma[p] = one(T) / kappa
82+
elseif mode == 3
83+
factor = kappa^(-1 / (p - 1))
84+
sigma = factor .^ [0:p-1;]
85+
elseif mode == 4
86+
sigma = ones(T, p) - T[0:p-1;] / (p - 1) * (1 - 1 / kappa)
87+
elseif mode == 5
88+
sigma = exp.(-rand(p) * log(kappa))
89+
end
90+
91+
M = zeros(T, m, n)
92+
M[1:p, 1:p] = diagm(0 => sigma)
93+
M = qmult!(copy(M'))
94+
M = qmult!(copy(M'))
95+
96+
return new{T}(m, n, kappa, M)
97+
end
98+
end
99+
100+
# constructors
101+
RandSVD(n::Integer) = RandSVD(n, sqrt(1 / eps()))
102+
RandSVD(n::Integer, kappa::Number) = RandSVD(n, kappa, 3)
103+
RandSVD(n::Integer, kappa::Number, mode::Integer) = RandSVD(n, n, kappa, mode)
104+
RandSVD(m::Integer, n::Integer, kappa::T, mode::Integer) where {T<:Number} = RandSVD{T}(m, n, kappa, mode)
105+
RandSVD{T}(n::Integer) where {T} = RandSVD(n, sqrt(1 / eps(T)))
106+
RandSVD{T}(n::Integer, kappa::T) where {T} = RandSVD{T}(n, kappa, 3)
107+
RandSVD{T}(n::Integer, kappa::T, mode::Integer) where {T} = RandSVD{T}(n, n, kappa, mode)
108+
109+
# metadata
110+
@properties RandSVD [:illcond, :random]
111+
112+
# properties
113+
size(A::RandSVD) = (A.m, A.n)
114+
115+
# functions
116+
@inline Base.@propagate_inbounds function getindex(A::RandSVD{T}, i::Integer, j::Integer) where {T}
117+
@boundscheck checkbounds(A, i, j)
118+
return A.M[i, j]
119+
end

0 commit comments

Comments
 (0)