5
5
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6
6
7
7
"""
8
- Constrains τ to
9
- τ ⪰ e' * X^{1/2} * logm(X^{1/2}*Y^{-1}*X^{1/2}) * X^{1/2} * e
10
-
11
- This function implements the semidefinite programming approximation given in
12
- the reference below. Parameters m and k control the accuracy of this
13
- approximation: m is the number of quadrature nodes to use and k the number
14
- of square-roots to take. See reference for more details.
15
-
16
- All expressions and atoms are subtypes of AbstractExpr.
17
- Please read expressions.jl first.
18
-
19
- REFERENCE
20
- Ported from CVXQUAD which is based on the paper: "Semidefinite
21
- approximations of matrix logarithm" by Hamza Fawzi, James Saunderson and
22
- Pablo A. Parrilo (arXiv:1705.00812)
23
- """
24
- mutable struct RelativeEntropyEpiCone
25
- X:: AbstractExpr
26
- Y:: AbstractExpr
27
- m:: Integer
28
- k:: Integer
29
- e:: AbstractMatrix
30
- size:: Tuple{Int,Int}
31
-
32
- function RelativeEntropyEpiCone (
33
- X:: AbstractExpr ,
34
- Y:: AbstractExpr ,
8
+ RelativeEntropyEpiConeSquare(
9
+ side_dimension::Int,
35
10
m::Integer = 3,
36
11
k::Integer = 3,
37
- e:: AbstractArray = Matrix (1.0 * LinearAlgebra. I, size (X )),
12
+ e::AbstractArray = Matrix(1.0 * LinearAlgebra.I(side_dimension )),
38
13
)
39
- if size (X) != size (Y)
40
- throw (DimensionMismatch (" X and Y must be the same size" ))
41
- end
42
- n = size (X)[1 ]
43
- if size (X) != (n, n)
44
- throw (DimensionMismatch (" X and Y must be square" ))
45
- end
46
- if length (size (e)) == 1
47
- e = reshape (e, (size (e)[1 ], 1 ))
48
- end
49
- erows, ecols = size (e)
50
- if ndims (e) != 2 || erows != n
51
- throw (DimensionMismatch (" e matrix must have n rows" ))
52
- end
53
- return new (X, Y, m, k, e, (ecols, ecols))
54
- end
55
14
56
- function RelativeEntropyEpiCone (
57
- X:: Value ,
58
- Y:: AbstractExpr ,
59
- m:: Integer = 3 ,
60
- k:: Integer = 3 ,
61
- e:: AbstractArray = Matrix (1.0 * LinearAlgebra. I, size (X)),
62
- )
63
- return RelativeEntropyEpiCone (constant (X), Y, m, k, e)
64
- end
15
+ Constrains `(τ, X, Y)` to:
16
+ ```
17
+ τ ⪰ e' * X^{1/2} * logm(X^{1/2}*Y^{-1}*X^{1/2}) * X^{1/2} * e
18
+ ```
65
19
66
- function RelativeEntropyEpiCone (
67
- X:: AbstractExpr ,
68
- Y:: Value ,
69
- m:: Integer = 3 ,
70
- k:: Integer = 3 ,
71
- e:: AbstractArray = Matrix (1.0 * LinearAlgebra. I, size (X)),
72
- )
73
- return RelativeEntropyEpiCone (X, constant (Y), m, k, e)
74
- end
20
+ This set implements the semidefinite programming approximation given in the
21
+ reference below.
75
22
76
- function RelativeEntropyEpiCone (
77
- X:: Value ,
78
- Y:: Value ,
79
- m:: Integer = 3 ,
80
- k:: Integer = 3 ,
81
- e:: AbstractArray = Matrix (1.0 * LinearAlgebra. I, size (X)),
82
- )
83
- return RelativeEntropyEpiCone (constant (X), constant (Y), m, k, e)
84
- end
85
- end
23
+ Parameters `m` and `k` control the accuracy of this approximation: `m` is the
24
+ number of quadrature nodes to use and `k` the number of square-roots to take.
25
+ See reference for more details.
86
26
87
- mutable struct RelativeEntropyEpiConeConstraint <: Constraint
88
- τ:: AbstractExpr
89
- cone:: RelativeEntropyEpiCone
27
+ ## Reference
90
28
91
- function RelativeEntropyEpiConeConstraint (
92
- τ:: AbstractExpr ,
93
- cone:: RelativeEntropyEpiCone ,
29
+ Ported from CVXQUAD which is based on the paper: "Semidefinite approximations of
30
+ matrix logarithm" by Hamza Fawzi, James Saunderson and Pablo A. Parrilo
31
+ (arXiv:1705.00812)
32
+ """
33
+ struct RelativeEntropyEpiConeSquare{T} <: MOI.AbstractVectorSet
34
+ side_dimension:: Int
35
+ m:: Int
36
+ k:: Int
37
+ e:: Matrix{T}
38
+
39
+ function RelativeEntropyEpiConeSquare (
40
+ side_dimension:: Int ,
41
+ m:: Integer = 3 ,
42
+ k:: Integer = 3 ,
43
+ e:: AbstractArray = Matrix (1.0 * LinearAlgebra. I (side_dimension)),
94
44
)
95
- if size (τ) != cone . size
96
- throw ( DimensionMismatch ( " τ must be size $(cone . size) " ))
45
+ if length ( size (e)) == 1
46
+ e = reshape (e, ( size (e, 1 ), 1 ))
97
47
end
98
- return new (τ, cone)
99
- end
100
-
101
- function RelativeEntropyEpiConeConstraint (
102
- τ:: Value ,
103
- cone:: RelativeEntropyEpiCone ,
104
- )
105
- return RelativeEntropyEpiConeConstraint (constant (τ), cone)
48
+ if ndims (e) != 2 || size (e, 1 ) != side_dimension
49
+ throw (DimensionMismatch (" e matrix must have $side_dimension rows" ))
50
+ end
51
+ return new {eltype(e)} (side_dimension, m, k, e)
106
52
end
107
53
end
108
54
109
- function iscomplex (c :: RelativeEntropyEpiConeConstraint )
110
- return iscomplex (c . τ) || iscomplex (c . cone)
55
+ function MOI . dimension (set :: RelativeEntropyEpiConeSquare )
56
+ return size (set . e, 2 ) ^ 2 + 2 * set . side_dimension ^ 2
111
57
end
112
58
113
- iscomplex (c:: RelativeEntropyEpiCone ) = iscomplex (c. X) || iscomplex (c. Y)
114
-
115
- function head (io:: IO , :: RelativeEntropyEpiConeConstraint )
116
- return print (io, " ∈(RelativeEntropyEpiCone)" )
59
+ function head (io:: IO , :: RelativeEntropyEpiConeSquare )
60
+ return print (io, " RelativeEntropyEpiConeSquare" )
117
61
end
118
62
119
- function Base. in (τ, cone:: RelativeEntropyEpiCone )
120
- return RelativeEntropyEpiConeConstraint (τ, cone)
63
+ function GenericConstraint (func:: Tuple , set:: RelativeEntropyEpiConeSquare )
64
+ @assert length (func) == 3
65
+ sizes = (size (set. e, 2 ), set. side_dimension, set. side_dimension)
66
+ for (i, f) in enumerate (func)
67
+ n = LinearAlgebra. checksquare (f)
68
+ if n != sizes[i]
69
+ throw (
70
+ DimensionMismatch (
71
+ " Matrix of side dimension `$n ` does not match required side dimension `$(sizes[i]) `" ,
72
+ ),
73
+ )
74
+ end
75
+ end
76
+ return GenericConstraint (vcat (vec .(func)... ), set)
121
77
end
122
78
123
- function AbstractTrees. children (constraint:: RelativeEntropyEpiConeConstraint )
124
- return (constraint. τ, constraint. cone. X, constraint. cone. Y)
79
+ function _get_matrices (c:: GenericConstraint{<:RelativeEntropyEpiConeSquare} )
80
+ n_τ, n_x = size (c. set. e, 2 ), c. set. side_dimension
81
+ d_τ, d_x = n_τ^ 2 , n_x^ 2
82
+ τ = reshape (c. child[1 : d_τ], n_τ, n_τ)
83
+ X = reshape (c. child[d_τ.+ (1 : d_x)], n_x, n_x)
84
+ Y = reshape (c. child[d_τ.+ d_x.+ (1 : d_x)], n_x, n_x)
85
+ return τ, X, Y
125
86
end
126
87
127
88
# This negative relative entropy function is matrix convex (arxiv:1705.00812).
128
89
# So if X and Y are convex sets, then τ ⪰ -D_op(X || Y) will be a convex set.
129
- function vexity (constraint:: RelativeEntropyEpiConeConstraint )
130
- X = vexity (constraint. cone. X)
131
- Y = vexity (constraint. cone. Y)
132
- τ = vexity (constraint. τ)
133
- # NOTE: can't say X == NotDcp() because the NotDcp constructor prints a warning message.
134
- if typeof (X) == ConcaveVexity || typeof (X) == NotDcp
135
- return NotDcp ()
136
- end
137
- if typeof (Y) == ConcaveVexity || typeof (Y) == NotDcp
138
- return NotDcp ()
139
- end
140
- vex = ConvexVexity () + (- τ)
141
- if vex == ConcaveVexity ()
90
+ function vexity (constraint:: GenericConstraint{<:RelativeEntropyEpiConeSquare} )
91
+ τ, X, Y = _get_matrices (constraint)
92
+ if vexity (X) in (ConcaveVexity (), NotDcp ()) ||
93
+ vexity (Y) in (ConcaveVexity (), NotDcp ())
142
94
return NotDcp ()
143
95
end
144
- return vex
96
+ return ConvexVexity () + - vexity (τ)
145
97
end
146
98
147
- function glquad (m)
148
- # Compute Gauss-Legendre quadrature nodes and weights on [0, 1]
149
- # Code below is from [Trefethen, "Is Gauss quadrature better than
150
- # Clenshaw-Curtis?", SIAM Review 2008] and computes the weights and
151
- # nodes on [-1, 1].
152
- beta = [0.5 ./ sqrt (1 - (2 * i)^- 2 ) for i in 1 : (m- 1 )] # 3-term recurrence coeffs
153
- T = LinearAlgebra. diagm (1 => beta, - 1 => beta) # Jacobi matrix
99
+ """
100
+ Compute Gauss-Legendre quadrature nodes and weights on [0, 1].
101
+
102
+ Code below is from Trefethen (2008), "Is Gauss quadrature better than
103
+ Clenshaw-Curtis?", SIAM Review, and computes the weights and nodes on [-1, 1].
104
+ """
105
+ function _gauss_legendre_quadrature (m)
106
+ # 3-term recurrence coeffs
107
+ beta = [0.5 ./ sqrt (1 - (2 * i)^- 2 ) for i in 1 : (m- 1 )]
108
+ # Jacobi matrix
109
+ T = LinearAlgebra. diagm (1 => beta, - 1 => beta)
154
110
s, V = LinearAlgebra. eigen (T)
155
- w = 2 * V[1 , :] .^ 2 # weights
111
+ w = 2 * V[1 , :] .^ 2
156
112
# Translate and scale to [0, 1]
157
113
s = (s .+ 1 ) / 2
158
114
w = w' / 2
@@ -161,17 +117,12 @@ end
161
117
162
118
function _add_constraint! (
163
119
context:: Context ,
164
- constraint:: RelativeEntropyEpiConeConstraint ,
120
+ constraint:: GenericConstraint{<:RelativeEntropyEpiConeSquare} ,
165
121
)
166
- X = constraint. cone. X
167
- Y = constraint. cone. Y
168
- m = constraint. cone. m
169
- k = constraint. cone. k
170
- e = constraint. cone. e
171
- τ = constraint. τ
172
- n = size (X)[1 ]
173
- r = size (e)[2 ]
174
- s, w = glquad (m)
122
+ τ, X, Y = _get_matrices (constraint)
123
+ m, k, e = constraint. set. m, constraint. set. k, constraint. set. e
124
+ n, r = size (X, 1 ), size (e, 2 )
125
+ s, w = _gauss_legendre_quadrature (m)
175
126
is_complex =
176
127
sign (X) == ComplexSign () ||
177
128
sign (Y) == ComplexSign () ||
@@ -183,25 +134,17 @@ function _add_constraint!(
183
134
Z = Variable (n, n)
184
135
T = [Variable (r, r) for i in 1 : m]
185
136
end
186
- add_constraint! (
187
- context,
188
- GenericConstraint (
189
- (Z, X, Y),
190
- GeometricMeanHypoConeSquare (1 // (2 ^ k), n, false ),
191
- ),
192
- )
137
+ set = GeometricMeanHypoConeSquare (1 // (2 ^ k), n, false )
138
+ add_constraint! (context, GenericConstraint ((Z, X, Y), set))
193
139
for ii in 1 : m
194
- # Note that we are dividing by w here because it is easier
195
- # to do this than to do sum w_i T(:,...,:,ii) later (cf. line that
196
- # involves τ)
197
- add_constraint! (
198
- context,
199
- [
200
- e' * X* e- s[ii]* T[ii]/ w[ii] e' * X
201
- X* e (1 - s[ii])* X+ s[ii]* Z
202
- ] ⪰ 0 ,
203
- )
140
+ # Note that we are dividing by w here because it is easier to do this
141
+ # than to do sum w_i T(:,...,:,ii) later (cf. line that involves τ)
142
+ A_ii = [
143
+ e' * X* e- s[ii]* T[ii]/ w[ii] e' * X
144
+ X* e (1 - s[ii])* X+ s[ii]* Z
145
+ ]
146
+ add_constraint! (context, A_ii ⪰ 0 )
204
147
end
205
- add_constraint! (context, ( 2 ^ k) * sum (T) + τ ⪰ 0 )
148
+ add_constraint! (context, 2 ^ k * sum (T) + τ ⪰ 0 )
206
149
return
207
150
end
0 commit comments