@@ -3,7 +3,7 @@ using LinearAlgebra
3
3
using LinearAlgebra: givensAlgorithm
4
4
5
5
# # EigenQ
6
- struct EigenQ{T} <: AbstractMatrix{T}
6
+ struct EigenQ{T}
7
7
uplo:: Char
8
8
factors:: Matrix{T}
9
9
τ:: Vector{T}
23
23
function LinearAlgebra. lmul! (Q:: EigenQ , B:: StridedVecOrMat )
24
24
m, n = size (B)
25
25
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 " ))
27
27
end
28
28
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
39
38
end
40
39
end
40
+ h[1 ] = tmp
41
41
end
42
42
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
53
52
end
54
53
end
54
+ h[end ] = tmp
55
55
end
56
56
else
57
57
throw (ArgumentError (" Q.uplo is neither 'L' or 'U'. This should never happen." ))
62
62
function LinearAlgebra. rmul! (A:: StridedMatrix , Q:: EigenQ )
63
63
m, n = size (A)
64
64
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 " ))
66
66
end
67
67
if Q. uplo == ' L'
68
68
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]'
79
77
end
80
78
end
79
+ h[1 ] = tmp
81
80
end
82
- elseif Q. uplo == ' U' # FixMe! Should consider reordering loops
81
+ elseif Q. uplo == ' U'
83
82
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]'
94
91
end
95
92
end
93
+ h[end ] = tmp
96
94
end
97
95
else
98
96
throw (ArgumentError (" Q.uplo is neither 'L' or 'U'. This should never happen." ))
99
97
end
100
98
return A
101
99
end
102
100
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 )))
104
102
105
103
106
104
# # SymmetricTridiagonalFactorization
@@ -463,49 +461,37 @@ function symtriLower!(
463
461
464
462
@inbounds for k = 1 : (n- 2 + ! (T <: Real ))
465
463
466
- τk = LinearAlgebra. reflector! (view (AS, (k+ 1 ): n, k))
464
+ x = view (AS, (k + 1 ): n, k)
465
+
466
+ τk = LinearAlgebra. reflector! (x)
467
467
τ[k] = τk
468
468
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
485
472
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)
491
475
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))
496
478
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ⱼ'
507
490
end
508
491
end
492
+
493
+ # Restore element
494
+ x[1 ] = tmp
509
495
end
510
496
SymmetricTridiagonalFactorization (
511
497
EigenQ (
@@ -531,37 +517,41 @@ function symtriUpper!(
531
517
end
532
518
533
519
@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 )
548
525
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ⱼ'
562
550
end
563
- AS[j, j] -= 2 * real (hjt' * ujt) - abs2 (hjt) * ξ
564
551
end
552
+
553
+ # Restore element
554
+ x[end ] = tmp
565
555
end
566
556
SymmetricTridiagonalFactorization (
567
557
EigenQ (
0 commit comments