Skip to content

Commit a90d2b2

Browse files
authored
Audit usages of evaluate (#655)
1 parent c17b411 commit a90d2b2

File tree

7 files changed

+22
-0
lines changed

7 files changed

+22
-0
lines changed

docs/src/developer/contributing.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ Convex.jl. Let's say you're adding the new function $f$.
4040
- Ensure the atom is a mutable struct, so that `objectid` can be called.
4141
- Add as a comment a description of what the atom does and its
4242
parameters.
43+
- Ensure `evaluate` is only called during the definition of `evaluate` itself, from `conic_form!`, or
44+
on constants or matrices. Specifically, `evaluate` must not be called on a potentially `fix!`'d variable
45+
when the expression tree is being built (for example, when constructing an atom or reformulating),
46+
since then any changes to the variable's value (or it being `free!`'d) will not be recognized.
47+
See [#653](https://github.com/jump-dev/Convex.jl/issues/653) and [#585](https://github.com/jump-dev/Convex.jl/issues/585)
48+
for previous bugs caused by misuse here.
4349
- The most mathematically interesting part is the `new_conic_form!`
4450
function. Following the example in the nuclear norm atom, you'll
4551
see that you can just construct the problem whose optimal value is

src/atoms/QuantumRelativeEntropyAtom.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ function quantum_relative_entropy(
159159
k::Integer = 3,
160160
nullspace_tol::Real = 1e-6,
161161
)
162+
# This `evaluate` is safe since it is not a `fix!`ed variable
163+
# (it must be a constant or matrix)
162164
return QuantumRelativeEntropy2Atom(A, evaluate(B), m, k, nullspace_tol)
163165
end
164166

@@ -168,6 +170,8 @@ function quantum_relative_entropy(
168170
m::Integer = 3,
169171
k::Integer = 3,
170172
)
173+
# This `evaluate` is safe since it is not a `fix!`ed variable
174+
# (it must be a constant or matrix)
171175
A = evaluate(A)
172176
return -quantum_entropy(A, m, k) - trace_logm(B, A, m, k)
173177
end
@@ -179,6 +183,8 @@ function quantum_relative_entropy(
179183
k::Integer = 0,
180184
nullspace_tol::Real = 1e-6,
181185
)
186+
# These `evaluate`s are safe since it is not a `fix!`ed variable
187+
# (it must be a constant or matrix)
182188
A, B = evaluate(A), evaluate(B)
183189
if size(A) != size(B)
184190
throw(DimensionMismatch("A and B must be the same size"))

src/atoms/TraceLogmAtom.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ function trace_logm(
7979
m::Integer = 3,
8080
k::Integer = 3,
8181
)
82+
# This `evaluate` is safe since it is not a `fix!`ed variable
83+
# (it must be a constant or matrix)
8284
return TraceLogmAtom(X, evaluate(C), m, k)
8385
end
8486

src/atoms/TraceMpowerAtom.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ function trace_mpower(
6464
t::Rational,
6565
C::Union{AbstractMatrix,Constant},
6666
)
67+
# This `evaluate` is safe since it is not a `fix!`ed variable
68+
# (it must be a constant or matrix)
6769
return TraceMpowerAtom(A, t, evaluate(C))
6870
end
6971

src/constraints/GenericConstraint.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ end
8686

8787
function _add_constraint!(context::Context, c::GenericConstraint)
8888
if vexity(c.child) == ConstVexity()
89+
# This `evaluate` call is safe, since even if it refers to a `fix!`'d variable,
90+
# it happens when we are formulating the problem (not at expression-time), so there
91+
# is not time for the variable to be re-`fix!`'d to a different value (or `free!`'d)
8992
if !is_feasible(evaluate(c.child), c.set, CONSTANT_CONSTRAINT_TOL[])
9093
context.detected_infeasible_during_formulation = true
9194
end

src/reformulations/quadform.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ function quadform(x::AbstractExpr, A::Value; assume_psd = false)
8585
return factor * square(norm2(P * x))
8686
end
8787

88+
# These `evaluate` calls are safe since they are not a `fix!`ed variable
89+
# (must be a constant):
8890
function quadform(x::Constant, A::AbstractExpr; kwargs...)
8991
return quadform(evaluate(x), A; kwargs...)
9092
end

src/reformulations/transpose.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ end
1212

1313
LinearAlgebra.adjoint(x::AbstractExpr) = transpose(conj(x))
1414

15+
# These `evaluate` calls are safe since they are not a `fix!`ed variable:
1516
function LinearAlgebra.transpose(x::Union{Constant,ComplexConstant})
1617
return constant(transpose(evaluate(x)))
1718
end

0 commit comments

Comments
 (0)