-
Notifications
You must be signed in to change notification settings - Fork 19
/
lowrankricatti.jl
125 lines (102 loc) · 3.06 KB
/
lowrankricatti.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# implement Mena et al. for solving
# (d P)/(d t) = A P(t) + P(t) A' + Q, P(t_0) = P0
# with a low rank approximation for Q
using SparseArrays
using SuiteSparse
using LinearAlgebra
using Expokit # for matrix exponentials
using BenchmarkTools
# construct an example
d = 800
Q = sprand(d,d,.01) - I# Diagonal(randn(d))
diagels = 0.4.^(1:d)
A = sprand(d,d,0.1)#Diagonal(diagels)
P0 = Matrix(sprand(d,d,0.1))# Diagonal(diagels)
# we use Expokit, which gives the funciton expmv!
# w = expmv!{T}( w::Vector{T}, t::Number, A, v::Vector{T}; kwargs...)
# The function expmv! calculates w = exp(t*A)*v, where A is a matrix or any type that supports size, eltype and mul! and v is a dense vector by using Krylov subspace projections. The result is stored in w.
# It is much faster for large systems
v = rand(d)
@time a1=expmv!(zeros(d),1,Q,v)
@time a2=exp(Matrix(Q))*v
a1-a2
# Let's consider a few implementations
matexpvec1 = function(A,U,τ)
exp(Matrix(τ * A)) * U
end
matexpvec2 = function(A,U,τ)
M = []
for j in 1:size(U,2)
push!(M, expmv!(zeros(size(U,1)),τ,A,U[:,j]))
end
M = hcat(M...)
end
# matexpvec3 = function(A,U) # is wrong for some reason
# τ = 1.0
# for j in 1:size(U,2)
# expmv!(U[:,j],τ,A,U[:,j])
# end
# U
# end
matexpvec4 = function(A,U,τ)
hcat([ expmv!(U[:,j],τ,A,U[:,j]) for j in 1:size(U,2)]...)
end
τ = 1.0
R = A # or Q
M0 = svd(P0)
U = Matrix(M0.U[:,1:ld])
@benchmark matexpvec1(R,U,τ)
@benchmark matexpvec2(R,U,τ)
@benchmark matexpvec4(R,U,τ)
maximum(matexpvec4(R,U,τ)-matexpvec1(R,U,τ))
function lowrankricatti(P0, A, Q, t, ld)
# P0 is the value of P at time t[1]; t is an increasing sequence of times,
# ld is the dimension of the low rank approximation
# output: at each time t a low rank approximation of P(t), where the couple (U,S) is such that P = U*S*U'
# step 1
M0 = svd(P0)
global U = Matrix(M0.U[:,1:ld])
global S = Diagonal(M0.S[1:ld])
Ulowrank = [U]
Slowrank = [S]
for i in 1:length(t)-1
τ = t[i+1] - t[i]
if false
matexp = exp(Matrix(τ * A))
# step 3
M = matexp * U
else
M = matexpvec4(A,U,τ)
end
Uᴬcompact, R = qr(M)
Uᴬ = Matrix(Uᴬcompact)
# step 4
Sᴬ = R * S * R'
# step 5
K = Uᴬ * Sᴬ + (τ * Q) * Uᴬ
Ucompact, Ŝ = qr(K)
U = Matrix(Ucompact)
Ŝ = Ŝ - U' * (τ * Q) * Uᴬ
L = Ŝ * Uᴬ' + U' * (τ * Q)
S = L * U
push!(Ulowrank,U)
push!(Slowrank,Diagonal(S))
# ??? should S be diagonal or not: either convert it, or make Slowrank an array of full matrices
#push!(Slowrank,S)
end
Ulowrank, Slowrank
end
# Example:
ld = 10 # low rank dimension
τ = 0.01; t0 =0.5; tend=0.8
t = t0:τ:tend
@time U,S = lowrankricatti(P0,A, Q, t,ld)
P = U[end]*S[end]*U[end]'
# compute exact solution
λ = lyap(Matrix(Q), -Matrix(A)) + P0
Φ = exp(Matrix(Q)*(tend- t0))
Pexact = Φ*λ*Φ' - λ + P0
# assess difference
dif = P - Pexact
print(maximum(abs.(dif)))
dif