Skip to content

Commit 8ad02eb

Browse files
authored
[breaking] refactor RelativeEntropyEpiCone into RelativeEntropyEpiConeSquare (#663)
* [breaking] refactor RelativeEntropyEpiCone into RelativeEntropyEpiConeSquare * Update * Update * Update * Update * Update
1 parent 5b968d1 commit 8ad02eb

File tree

8 files changed

+157
-325
lines changed

8 files changed

+157
-325
lines changed

docs/src/manual/operations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ solver (including SCS and Mosek).
123123
| `trace_logm(X, C)` | $\textrm{Tr}(C \log X)$ | concave in X | not monotonic | IC: $X \succeq 0$, $C \succeq 0$, $C$ constant; uses natural log |
124124
| `trace_mpower(A, t, C)` | $\textrm{Tr}(C A^t)$ | concave in A for $t \in [0,1]$, convex for $t \in [-1,0] \cup [1,2]$ | not monotonic | IC: $X \succeq 0$, $C \succeq 0$, $C$ constant, $t \in [-1, 2]$ |
125125
| `lieb_ando(A, B, K, t)` | $\textrm{Tr}(K' A^{1-t} K B^t)$ | concave in A,B for $t \in [0,1]$, convex for $t \in [-1,0] \cup [1,2]$ | not monotonic | IC: $A \succeq 0$, $B \succeq 0$, $K$ constant, $t \in [-1, 2]$ |
126-
| `T in RelativeEntropyEpiCone(X, Y, m, k, e)` | $T \succeq e' X^{1/2} \log(X^{1/2} Y^{-1} X^{1/2}) X^{1/2} e$ | convex | not monotonic | IC: $e$ constant; uses natural log |
126+
| `Convex.GenericConstraint((T, X, Y), RelativeEntropyEpiConeSquare(size(X, 1), m, k, e))` | $T \succeq e' X^{1/2} \log(X^{1/2} Y^{-1} X^{1/2}) X^{1/2} e$ | convex | not monotonic | IC: $e$ constant; uses natural log |
127127

128128
## Exponential + SDP representable Functions
129129

src/Convex.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export conv,
4646
sumsquares,
4747
GeometricMeanHypoConeSquare,
4848
GeometricMeanEpiConeSquare,
49-
RelativeEntropyEpiCone,
49+
RelativeEntropyEpiConeSquare,
5050
quantum_relative_entropy,
5151
quantum_entropy,
5252
trace_logm,

src/atoms/QuantumEntropyAtom.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ function new_conic_form!(
8181
Variable(size(X))
8282
end
8383
I = Matrix(one(T) * LinearAlgebra.I(size(X, 1)))
84-
add_constraint!(context, τ in RelativeEntropyEpiCone(X, I, atom.m, atom.k))
84+
set = RelativeEntropyEpiConeSquare(size(X, 1), atom.m, atom.k)
85+
add_constraint!(context, GenericConstraint((τ, X, I), set))
8586
# It's already a real mathematically, but need to make it a real type.
8687
return conic_form!(context, real(-LinearAlgebra.tr(τ)))
8788
end

src/atoms/QuantumRelativeEntropyAtom.jl

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,9 @@ function new_conic_form!(
9393
add_constraint!(context, A 0)
9494
add_constraint!(context, B 0)
9595
I = Matrix(one(T) * LinearAlgebra.I(size(A, 1)))
96-
m, k, e = atom.m, atom.k, vec(I)
97-
τ = Variable()
98-
add_constraint!(
99-
context,
100-
τ in RelativeEntropyEpiCone(kron(A, I), kron(I, conj(B)), m, k, e),
101-
)
96+
τ, X, Y = Variable(), kron(A, I), kron(I, conj(B))
97+
set = RelativeEntropyEpiConeSquare(size(X, 1), atom.m, atom.k, vec(I))
98+
add_constraint!(context, GenericConstraint((τ, X, Y), set))
10299
return conic_form!(context, τ)
103100
end
104101

src/atoms/TraceLogmAtom.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ function new_conic_form!(context::Context{T}, atom::TraceLogmAtom) where {T}
102102
Variable(size(X))
103103
end
104104
I = Matrix(one(T) * LinearAlgebra.I(size(X, 1)))
105-
add_constraint!(context, τ in RelativeEntropyEpiCone(I, X, atom.m, atom.k))
105+
set = RelativeEntropyEpiConeSquare(size(X, 1), atom.m, atom.k)
106+
add_constraint!(context, GenericConstraint((τ, I, X), set))
106107
# It's already a real mathematically, but need to make it a real type.
107108
return conic_form!(context, real(-LinearAlgebra.tr(atom.C * τ)))
108109
end

src/constraints/RelativeEntropyEpiCone.jl

Lines changed: 91 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -5,154 +5,110 @@
55
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
66

77
"""
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,
3510
m::Integer = 3,
3611
k::Integer = 3,
37-
e::AbstractArray = Matrix(1.0 * LinearAlgebra.I, size(X)),
12+
e::AbstractArray = Matrix(1.0 * LinearAlgebra.I(side_dimension)),
3813
)
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
5514
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+
```
6519
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.
7522
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.
8626
87-
mutable struct RelativeEntropyEpiConeConstraint <: Constraint
88-
τ::AbstractExpr
89-
cone::RelativeEntropyEpiCone
27+
## Reference
9028
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)),
9444
)
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))
9747
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)
10652
end
10753
end
10854

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
11157
end
11258

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")
11761
end
11862

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)
12177
end
12278

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
12586
end
12687

12788
# This negative relative entropy function is matrix convex (arxiv:1705.00812).
12889
# 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())
14294
return NotDcp()
14395
end
144-
return vex
96+
return ConvexVexity() + -vexity(τ)
14597
end
14698

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)
154110
s, V = LinearAlgebra.eigen(T)
155-
w = 2 * V[1, :] .^ 2 # weights
111+
w = 2 * V[1, :] .^ 2
156112
# Translate and scale to [0, 1]
157113
s = (s .+ 1) / 2
158114
w = w' / 2
@@ -161,17 +117,12 @@ end
161117

162118
function _add_constraint!(
163119
context::Context,
164-
constraint::RelativeEntropyEpiConeConstraint,
120+
constraint::GenericConstraint{<:RelativeEntropyEpiConeSquare},
165121
)
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)
175126
is_complex =
176127
sign(X) == ComplexSign() ||
177128
sign(Y) == ComplexSign() ||
@@ -183,25 +134,17 @@ function _add_constraint!(
183134
Z = Variable(n, n)
184135
T = [Variable(r, r) for i in 1:m]
185136
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))
193139
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)
204147
end
205-
add_constraint!(context, (2^k) * sum(T) + τ 0)
148+
add_constraint!(context, 2^k * sum(T) + τ 0)
206149
return
207150
end

0 commit comments

Comments
 (0)