@@ -3,7 +3,7 @@ using LinearAlgebra
33using LinearAlgebra: givensAlgorithm
44
55# # EigenQ
6- struct EigenQ{T} <: AbstractMatrix{T}
6+ struct EigenQ{T}
77 uplo:: Char
88 factors:: Matrix{T}
99 τ:: Vector{T}
2323function LinearAlgebra. lmul! (Q:: EigenQ , B:: StridedVecOrMat )
2424 m, n = size (B)
2525 if size (Q, 1 ) != m
26- throw (DimensionMismatch (" " ))
26+ throw (DimensionMismatch (" first dimension of second argument matrix doesn't match the size of the first argument reflectors " ))
2727 end
2828 if Q. uplo == ' L'
29- @inbounds for k = length (Q. τ): - 1 : 1
30- for j = 1 : size (B, 2 )
31- vb = B[k+ 1 , j]
32- for i = (k+ 2 ): m
33- vb += Q. factors[i, k]' B[i, j]
34- end
35- τkvb = Q. τ[k]' vb
36- B[k+ 1 , j] -= τkvb
37- for i = (k+ 2 ): m
38- B[i, j] -= Q. factors[i, k] * τkvb
29+ for k = length (Q. τ): - 1 : 1
30+ h = view (Q. factors, (k + 1 ): m, k)
31+ τ = Q. τ[k]
32+ tmp = h[1 ]
33+ h[1 ] = 1
34+ for j = 1 : n
35+ tmp = τ * (h' * view (B, (k + 1 ): m, j))
36+ for i = 1 : (m - k)
37+ B[k + i, j] -= h[i] * tmp
3938 end
4039 end
40+ h[1 ] = tmp
4141 end
4242 elseif Q. uplo == ' U'
43- @inbounds for k = length (Q. τ): - 1 : 1
44- for j = 1 : size (B, 2 )
45- vb = B[k+ 1 , j]
46- for i = (k+ 2 ): m
47- vb += Q. factors[k, i] * B[i, j]
48- end
49- τkvb = Q. τ[k]' vb
50- B[k+ 1 , j] -= τkvb
51- for i = (k+ 2 ): m
52- B[i, j] -= Q. factors[k, i]' * τkvb
43+ for k = length (Q. τ): - 1 : 1
44+ h = view (Q. factors, 1 : (m - k), m - k + 1 )
45+ τ = Q. τ[k]
46+ tmp = h[end ]
47+ h[end ] = 1
48+ for j = 1 : n
49+ tmp = τ * (h' * view (B, 1 : (n - k), j))
50+ for i = 1 : (n - k)
51+ B[i, j] -= h[i] * tmp
5352 end
5453 end
54+ h[end ] = tmp
5555 end
5656 else
5757 throw (ArgumentError (" Q.uplo is neither 'L' or 'U'. This should never happen." ))
6262function LinearAlgebra. rmul! (A:: StridedMatrix , Q:: EigenQ )
6363 m, n = size (A)
6464 if size (Q, 1 ) != n
65- throw (DimensionMismatch (" " ))
65+ throw (DimensionMismatch (" second dimension of first argument matrix doesn't match the size of the second argument reflectors " ))
6666 end
6767 if Q. uplo == ' L'
6868 for k = 1 : length (Q. τ)
69- for i = 1 : size (A, 1 )
70- a = view (A, i, :)
71- va = A[i, k+ 1 ]
72- for j = (k+ 2 ): n
73- va += A[i, j] * Q. factors[j, k]
74- end
75- τkva = va * Q. τ[k]'
76- A[i, k+ 1 ] -= τkva
77- for j = (k+ 2 ): n
78- A[i, j] -= τkva * Q. factors[j, k]'
69+ h = view (Q. factors, (k + 1 ): n, k)
70+ τ = Q. τ[k]
71+ tmp = h[1 ]
72+ h[1 ] = 1
73+ for i = 1 : m
74+ tmp = transpose (view (A, i, (k + 1 ): n)) * h * τ
75+ for j = 1 : (n - k)
76+ A[i, k + j] -= tmp * h[j]'
7977 end
8078 end
79+ h[1 ] = tmp
8180 end
82- elseif Q. uplo == ' U' # FixMe! Should consider reordering loops
81+ elseif Q. uplo == ' U'
8382 for k = 1 : length (Q. τ)
84- for i = 1 : size (A, 1 )
85- a = view (A, i, :)
86- va = A[i, k+ 1 ]
87- for j = (k+ 2 ): n
88- va += A[i, j] * Q. factors[k, j]'
89- end
90- τkva = va * Q. τ[k]'
91- A[i, k+ 1 ] -= τkva
92- for j = (k+ 2 ): n
93- A[i, j] -= τkva * Q. factors[k, j]
83+ h = view (Q. factors, 1 : (n - k), n - k + 1 )
84+ τ = Q. τ[k]
85+ tmp = h[end ]
86+ h[end ] = 1
87+ for i = 1 : m
88+ tmp = transpose (view (A, i, 1 : (n - k))) * h * τ
89+ for j = 1 : (n - k)
90+ A[i, j] -= tmp * h[j]'
9491 end
9592 end
93+ h[end ] = tmp
9694 end
9795 else
9896 throw (ArgumentError (" Q.uplo is neither 'L' or 'U'. This should never happen." ))
9997 end
10098 return A
10199end
102100
103- Base. Array (Q:: EigenQ ) = lmul! (Q, Matrix {eltype(Q) } (I, size (Q, 1 ), size (Q, 1 )))
101+ Base. Array (Q:: EigenQ{T} ) where T = lmul! (Q, Matrix {T } (I, size (Q, 1 ), size (Q, 1 )))
104102
105103
106104# # SymmetricTridiagonalFactorization
@@ -463,49 +461,37 @@ function symtriLower!(
463461
464462 @inbounds for k = 1 : (n- 2 + ! (T <: Real ))
465463
466- τk = LinearAlgebra. reflector! (view (AS, (k+ 1 ): n, k))
464+ x = view (AS, (k + 1 ): n, k)
465+
466+ τk = LinearAlgebra. reflector! (x)
467467 τ[k] = τk
468468
469- for i = (k+ 1 ): n
470- u[i] = AS[i, k+ 1 ]
471- end
472- for j = (k+ 2 ): n
473- ASjk = AS[j, k]
474- for i = j: n
475- u[i] += AS[i, j] * ASjk
476- end
477- end
478- for j = (k+ 1 ): (n- 1 )
479- tmp = zero (T)
480- for i = j+ 1 : n
481- tmp += AS[i, j]' AS[i, k]
482- end
483- u[j] += tmp
484- end
469+ # Temporarily store the implicit 1
470+ tmp = x[1 ]
471+ x[1 ] = 1
485472
486- vcAv = u[k+ 1 ]
487- for i = (k+ 2 ): n
488- vcAv += AS[i, k]' u[i]
489- end
490- ξτ2 = real (vcAv) * abs2 (τk) / 2
473+ Ã = view (AS, (k + 1 ): n, (k + 1 ): n)
474+ ũ = view (u, (k + 1 ): n)
491475
492- u[k+ 1 ] = u[k+ 1 ] * τk - ξτ2
493- for i = (k+ 2 ): n
494- u[i] = u[i] * τk - ξτ2 * AS[i, k]
495- end
476+ # Form Ãvτ
477+ mul! (ũ, Hermitian (Ã, :L ), x, τk, zero (τk))
496478
497- AS[k+ 1 , k+ 1 ] -= 2 real (u[k+ 1 ])
498- for i = (k+ 2 ): n
499- AS[i, k+ 1 ] -= u[i] + AS[i, k] * u[k+ 1 ]'
500- end
501- for j = (k+ 2 ): n
502- ASjk = AS[j, k]
503- uj = u[j]
504- AS[j, j] -= 2 real (uj * ASjk' )
505- for i = (j+ 1 ): n
506- AS[i, j] -= u[i] * ASjk' + AS[i, k] * uj'
479+ # Form τx'Ãvτ (which is real except for rounding)
480+ ξ = real (τk' * (x' * ũ))
481+
482+ # Compute the rank up and down grade
483+ # Ã = Ã + τx'Ãvτ - τx'Ã - Ãvτ
484+ for j in 1 : (n - k)
485+ xⱼ = x[j]
486+ ũⱼ = ũ[j]
487+ ξxⱼ = ξ * xⱼ
488+ for i in j: (n - k)
489+ AS[k + i, k + j] += x[i] * ξxⱼ' - x[i] * ũⱼ' - ũ[i] * xⱼ'
507490 end
508491 end
492+
493+ # Restore element
494+ x[1 ] = tmp
509495 end
510496 SymmetricTridiagonalFactorization (
511497 EigenQ (
@@ -531,37 +517,41 @@ function symtriUpper!(
531517 end
532518
533519 @inbounds for k = 1 : (n- 2 + ! (T <: Real ))
534- # This is a bit convoluted method to get the conjugated vector but conjugation is required for correctness of arrays of quaternions. Eventually, it should be sufficient to write vec(x') but it currently (July 10, 2018) hits a bug in LinearAlgebra
535- τk = LinearAlgebra. reflector! (vec (transpose (view (AS, k, (k+ 1 ): n)' )))
536- τ[k] = τk'
537-
538- for j = (k+ 1 ): n
539- tmp = AS[k+ 1 , j]
540- for i = (k+ 2 ): j
541- tmp += AS[k, i] * AS[i, j]
542- end
543- for i = (j+ 1 ): n
544- tmp += AS[k, i] * AS[j, i]'
545- end
546- u[j] = τk' * tmp
547- end
520+ # LAPACK allows the reflection element to be chosen freely whereas
521+ # LinearAlgebra's reflector! assumes it is the first element in the vector
522+ # Maybe we should implement a method similar to the LAPACK version
523+ x = view (AS, 1 : (n - k), n - k + 1 )
524+ xᵣ = view (AS, (n - k): - 1 : 1 , n - k + 1 )
548525
549- vcAv = u[k+ 1 ]
550- for i = (k+ 2 ): n
551- vcAv += u[i] * AS[k, i]'
552- end
553- ξ = real (vcAv * τk)
554-
555- for j = (k+ 1 ): n
556- ujt = u[j]
557- hjt = j > (k + 1 ) ? AS[k, j] : one (ujt)
558- ξhjt = ξ * hjt
559- for i = (k+ 1 ): (j- 1 )
560- hit = i > (k + 1 ) ? AS[k, i] : one (ujt)
561- AS[i, j] -= hit' * ujt + u[i]' * hjt - hit' * ξhjt
526+ τk = LinearAlgebra. reflector! (xᵣ)
527+ τ[k] = τk
528+
529+ # Temporarily store the implicit 1
530+ tmp = x[end ]
531+ x[end ] = 1
532+
533+ Ã = view (AS, 1 : (n - k), 1 : (n - k))
534+ ũ = view (u, 1 : (n - k))
535+
536+ # Form Ãvτ
537+ mul! (ũ, Hermitian (Ã, :U ), x, τk, zero (τk))
538+
539+ # Form τx'Ãvτ (which is real except for rounding)
540+ ξ = real (τk' * (x' * ũ))
541+
542+ # Compute the rank up and down grade
543+ # Ã = Ã + τx'Ãvτ - τx'Ã - Ãvτ
544+ for j in 1 : (n - k)
545+ xⱼ = x[j]
546+ ũⱼ = ũ[j]
547+ ξxⱼ = ξ * xⱼ
548+ for i in 1 : j
549+ AS[i, j] += x[i] * ξxⱼ' - x[i] * ũⱼ' - ũ[i] * xⱼ'
562550 end
563- AS[j, j] -= 2 * real (hjt' * ujt) - abs2 (hjt) * ξ
564551 end
552+
553+ # Restore element
554+ x[end ] = tmp
565555 end
566556 SymmetricTridiagonalFactorization (
567557 EigenQ (
0 commit comments