|
| 1 | +""" |
| 2 | +Sylvester's orthogonal matrix |
| 3 | +See Rosser matrix References 2. |
| 4 | +
|
| 5 | +for a = d = 2, b = c = 1, P_block' * P_block = 10 * Identity |
| 6 | +""" |
| 7 | +P_block(::Type{T}, a, b, c, d) where {T} = |
| 8 | + reshape(T[a, b, c, d, b, -a, -d, c, c, d, -a, -b, d, -c, b, -a], 4, 4) |
| 9 | + |
| 10 | +""" |
| 11 | +Rosser Matrix |
| 12 | +============= |
| 13 | +The Rosser matrix’s eigenvalues are very close together |
| 14 | + so it is a challenging matrix for many eigenvalue algorithms. |
| 15 | +
|
| 16 | +*Input options:* |
| 17 | +
|
| 18 | ++ dim, a, b: `dim` is the dimension of the matrix. |
| 19 | + `dim` must be a power of 2. |
| 20 | + `a` and `b` are scalars. For `dim = 8, a = 2` and `b = 1`, the generated |
| 21 | + matrix is the test matrix used by Rosser. |
| 22 | +
|
| 23 | ++ dim: `a = rand(1:5), b = rand(1:5)`. |
| 24 | +
|
| 25 | +*References:* |
| 26 | +
|
| 27 | +**J. B. Rosser, C. Lanczos, M. R. Hestenes, W. Karush**, |
| 28 | + Separation of close eigenvalues of a real symmetric matrix, |
| 29 | + Journal of Research of the National Bureau of Standards, v(47) |
| 30 | + (1951) |
| 31 | +""" |
| 32 | +struct Rosser{T<:Number} <: AbstractMatrix{T} |
| 33 | + n::Integer |
| 34 | + a::T |
| 35 | + b::T |
| 36 | + M::AbstractMatrix{T} |
| 37 | + |
| 38 | + function Rosser{T}(n::Integer, a::T, b::T) where {T<:Number} |
| 39 | + n >= 0 || throw(ArgumentError("$n < 0")) |
| 40 | + |
| 41 | + if n < 1 |
| 42 | + lgn = 0 |
| 43 | + else |
| 44 | + lgn = round(Integer, log2(n)) |
| 45 | + end |
| 46 | + 2^lgn != n && throw(ArgumentError("n must be positive integer and a power of 2.")) |
| 47 | + |
| 48 | + if n == 1 # handle 1-d case |
| 49 | + return 611 * ones(T, 1, 1) |
| 50 | + end |
| 51 | + |
| 52 | + if n == 2 |
| 53 | + #eigenvalues are 500, 510 |
| 54 | + B = T[101 1; 1 101] |
| 55 | + P = T[2 1; 1 -2] |
| 56 | + A = P' * B * P |
| 57 | + elseif n == 4 |
| 58 | + # eigenvalues are 0.1, 1019.9, 1020, 1020 for a = 2 and b = 1 |
| 59 | + B = zeros(T, n, n) |
| 60 | + B[1, 1], B[1, 4], B[4, 1], B[4, 4] = 101, 1, 1, 101 |
| 61 | + B[2, 2], B[2, 3], B[3, 2], B[3, 3] = 1, 10, 10, 101 |
| 62 | + P = P_block(T, a, b, b, a) |
| 63 | + A = P' * B * P |
| 64 | + elseif n == 8 |
| 65 | + # eigenvalues are 1020, 1020, 1000, 1000, 0.098, 0, -1020 |
| 66 | + B = zeros(T, n, n) |
| 67 | + B[1, 1], B[6, 1], B[2, 2], B[8, 2] = 102, 1, 101, 1 |
| 68 | + B[3, 3], B[7, 3] = 98, 14 |
| 69 | + B[4, 4], B[5, 4], B[4, 5], B[5, 5] = 1, 10, 10, 101 |
| 70 | + B[1, 6], B[6, 6], B[3, 7], B[7, 7], B[2, 8], B[8, 8] = 1, -102, 14, 2, 1, 101 |
| 71 | + P = [P_block(T, a, b, b, a)' zeros(T, 4, 4); zeros(T, 4, 4) P_block(T, b, -b, -a, a)] |
| 72 | + A = P' * B * P |
| 73 | + else |
| 74 | + lgn = lgn - 2 |
| 75 | + halfn = round(Integer, n / 2) |
| 76 | + # using Sylvester's method |
| 77 | + P = P_block(T, a, b, b, a) |
| 78 | + m = 4 |
| 79 | + for i in 1:lgn |
| 80 | + P = [P zeros(T, m, m); zeros(T, m, m) P] |
| 81 | + m = m * 2 |
| 82 | + end |
| 83 | + # mix 4 2-by-2 matrices (with close eigenvalues) into a large nxn matrix. |
| 84 | + B_list = T[102, 1, 1, -102, 101, 1, 1, 101, 1, 10, 10, 101, 98, 14, 14, 2] |
| 85 | + B = zeros(T, n, n) |
| 86 | + j, k = 1, 5 |
| 87 | + for i in 1:(halfn+1) |
| 88 | + indexend = halfn - 1 + i |
| 89 | + list_start = k |
| 90 | + list_end = k + 3 |
| 91 | + |
| 92 | + if list_start > 16 || list_end > 16 |
| 93 | + k = 1 |
| 94 | + list_start = 1 |
| 95 | + list_end = 4 |
| 96 | + end |
| 97 | + B[j, j], B[j, indexend], B[indexend, j], B[indexend, indexend] = B_list[list_start:list_end] |
| 98 | + j = j + 1 |
| 99 | + k = k + 4 |
| 100 | + end |
| 101 | + A = P' * B * P |
| 102 | + end |
| 103 | + |
| 104 | + return new{T}(n, a, b, A) |
| 105 | + end |
| 106 | +end |
| 107 | + |
| 108 | +# constructors |
| 109 | +Rosser(n::Integer) = Rosser(n, rand(1:5), rand(1:5)) |
| 110 | +Rosser(n::Integer, a::T, b::T) where {T<:Number} = Rosser{T}(n, a, b) |
| 111 | +Rosser{T}(n::Integer) where {T<:Number} = Rosser{T}(n, T(rand(1:5)), T(rand(1:5))) |
| 112 | + |
| 113 | +# metadata |
| 114 | +@properties Rosser [:illcond, :eigen, :random] |
| 115 | + |
| 116 | +# properties |
| 117 | +size(A::Rosser) = (A.n, A.n) |
| 118 | + |
| 119 | +# functions |
| 120 | +@inline Base.@propagate_inbounds function getindex(A::Rosser{T}, i::Integer, j::Integer) where {T} |
| 121 | + @boundscheck checkbounds(A, i, j) |
| 122 | + return A.M[i, j] |
| 123 | +end |
0 commit comments