-
Notifications
You must be signed in to change notification settings - Fork 0
/
uClarabel.jl
179 lines (153 loc) · 5.75 KB
/
uClarabel.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"using Clarabel (an interior point numerical solver) to do LP and QP"
module uClarabel
# promising, but too BIG, if the dep Pardiso can be removed, it will be perfect
using LinearAlgebra, Clarabel, SparseArrays, EfficientFrontier
export ClarabelQP, ClarabelLP
export ClarabelCL!
function SettingsCl(PS::Problem{T}; kwargs...) where {T}
if isempty(kwargs) #default setting
return Clarabel.Settings{T}(tol_gap_abs=2^-52, tol_gap_rel=2^-47, verbose=false)
end
Clarabel.Settings{T}(; kwargs...)
end
function SettingsCl(; kwargs...)
if isempty(kwargs) #default setting
return Clarabel.Settings(tol_gap_abs=2^-52, tol_gap_rel=2^-47, verbose=false)
end
Clarabel.Settings(; kwargs...)
end
function ClarabelQP(mu::T, PS::Problem{T}; settings=SettingsCl()) where {T}
(; E, V, u, d, G, g, A, b, N, M, J) = PS
iu = findall(u .< Inf)
P = sparse(V)
q = zeros(T, N) #Clarabel need P, q, A, b to be in type T
Ac = sparse([E'; A; G; -Matrix{T}(I, N, N); Matrix{T}(I, N, N)[iu, :]])
bc = [mu; b; g; -d; u[iu]]
cones = [Clarabel.ZeroConeT(1 + M), Clarabel.NonnegativeConeT(J + N + length(iu))]
solver = Clarabel.Solver{T}()
Clarabel.setup!(solver, P, q, Ac, bc, cones, settings)
Clarabel.solve!(solver)
end
#=
# LVEP (Lowest Variance Efficient Portfolio) == Global Minimum Variance Portfolio (GMVP)
function ClarabelQP(PS::Problem{T}; settings= SettingsCl()) where {T}
#function ClarabelQP(PS::Problem{T}; settings= SettingsCl(PS; verbose = false)) where {T}
(; V, u, d, G, g, A, b, N, M, J) = PS
iu = findall(u .< Inf)
P = sparse(V)
q = zeros(T, N) #Clarabel need P, q, A, b to be in type T
Ac = sparse([A; G; -Matrix{T}(I, N, N); Matrix{T}(I, N, N)[iu, :]])
bc = [b; g; -d; u[iu]]
cones = [Clarabel.ZeroConeT(M), Clarabel.NonnegativeConeT(J + N + length(iu))]
solver = Clarabel.Solver{T}()
Clarabel.setup!(solver, P, q, Ac, bc, cones, settings)
Clarabel.solve!(solver)
end
=#
function ClarabelQP(PS::Problem{T}, L::T=0.0; settings=SettingsCl()) where {T}
if isinf(L)
min = L == Inf ? false : true
x = ClarabelLP(PS; settings=settings, min=min)
return ClarabelQP(x.obj_val, PS; settings=settings)
end
(; E, V, u, d, G, g, A, b, N, M, J) = PS
iu = findall(u .< Inf)
P = sparse(V)
#q = zeros(T, N) #Clarabel need P, q, A, b to be in type T
Ac = sparse([A; G; -Matrix{T}(I, N, N); Matrix{T}(I, N, N)[iu, :]])
bc = [b; g; -d; u[iu]]
cones = [Clarabel.ZeroConeT(M), Clarabel.NonnegativeConeT(J + N + length(iu))]
solver = Clarabel.Solver{T}()
if L == 0
qq = zeros(T, N)
else
qq = -L * E
end
#=
if L == 0
Clarabel.setup!(solver, P, zeros(T, N), Ac, bc, cones, settings)
return Clarabel.solve!(solver)
end
if isfinite(L)
Clarabel.setup!(solver, P, -L * E, Ac, bc, cones, settings)
return Clarabel.solve!(solver)
end
=#
Clarabel.setup!(solver, P, qq, Ac, bc, cones, settings)
return Clarabel.solve!(solver)
end
#find the highest mean: -x.obj_val
function ClarabelLP(PS::Problem{T}; settings=SettingsCl(), min=true) where {T}
#function ClarabelLP(PS::Problem{T}; settings= SettingsCl(PS; verbose = false)) where {T}
(; E, u, d, G, g, A, b, N, M, J) = PS
iu = findall(u .< Inf)
P = spzeros(T, N, N)
#q = -E
#sgn = min == true ? 1 : -1
q = min ? E : -E
Ac = sparse([A; G; -Matrix{T}(I, N, N); Matrix{T}(I, N, N)[iu, :]])
bc = [b; g; -d; u[iu]]
cones = [Clarabel.ZeroConeT(M), Clarabel.NonnegativeConeT(J + N + length(iu))]
solver = Clarabel.Solver{T}()
Clarabel.setup!(solver, P, q, Ac, bc, cones, settings)
#Clarabel.setup!(solver, P, sgn * E, Ac, bc, cones, settings)
x = Clarabel.solve!(solver)
if !min
x.obj_val = -x.obj_val
end
return x
end
function getS(Y, PS, tolS)
#from slack variables
(; u, N, J) = PS
iu = findall(u .< Inf)
D = Y[(1:N).+J] .< tolS
U = falses(N)
U[iu] = Y[(1:length(iu)).+(J+N)] .< tolS
S = fill(IN, N + J)
Sv = @view S[1:N]
Sv[D] .= DN
Sv[U] .= UP
if J > 0
Se = @view S[(1:J).+N]
Se .= EO
Og = Y[1:J] .> tolS
Se[Og] .= OE
end
return S
end
"""
ClarabelCL!(aCL::Vector{sCL{T}}, PS::Problem{T}; nS=Settings(PS), settings=SettingsCl, kwargs...) where T
compute the Critical Line Segments by Clarabel.jl (Interior Point QP), for the highest expected return. Save the CL to aCL if done
"""
function ClarabelCL!(aCL::Vector{sCL{T}}, PS::Problem{T}; nS=Settings(PS), settings=SettingsCl(), kwargs...) where {T}
#function ClarabelCL!(aCL::Vector{sCL{T}}, PS::Problem{T}; nS=Settings(PS), settings=SettingsCl(PS; verbose = false), kwargs...) where {T}
x = ClarabelLP(PS; settings=settings, min=false)
if Int(x.status) != 1 #SOLVED
error("Not able to find the expected return of HMFP (Highest Mean Frontier Portfolio)")
end
#mu = -x.obj_val
mu = x.obj_val
shft = nS.muShft
if mu < -1 || mu > 1
shft *= abs(mu)
end
mu -= shft
y = ClarabelQP(mu, PS; settings=settings)
if Int(y.status) != 1 #SOLVED
error("Not able to find a muShft to the HMFP (Highest Mean Frontier Portfolio)")
end
Y = y.s[PS.M+2:end]
#S = EfficientFrontier.getS(Y, PS, nS.tolS)
S = getS(Y, PS, nS.tolS)
#=
# GMVP, fail to get correct S most of time. See https://github.com/oxfordcontrol/Clarabel.jl/issues/109 Corner Portfolio Blur
y = ClarabelQP(PS; settings=settings) #may fail, leave it to computeCL!
#= if Int(y.status) != 1 #SOLVED
error("Clarabel: Not able to find the Lowest Variance Efficient Portfolio")
end =#
Y = y.s[PS.M+1:end]
S = EfficientFrontier.getS(Y, PS, nS.tolS*128) =#
computeCL!(aCL, S, PS, nS)
end
end