Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimize ExpAtom #613

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions src/atoms/ExpAtom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,43 @@ function new_conic_form!(context::Context{T}, e::ExpAtom) where {T}
x = e.children[1]
m, n = size(x)
z = Variable(m, n)
for i in 1:m, j in 1:n
f = vcat(x[i, j], 1, z[i, j])
add_constraint!(context, GenericConstraint{MOI.ExponentialCone}(f))
# Naive implementation:
# for i in 1:m, j in 1:n
# f = vcat(x[i, j], 1, z[i, j])
# add_constraint!(context, GenericConstraint{MOI.ExponentialCone}(f))
# end
# return conic_form!(context, z)
# This is slow, since we are indexing on the Convex side, and convex is based around
# vector/matrix operations. We don't want to produce n*m IndexAtoms!
# Instead, we will drop to the MOI level to implement this in terms of scalar operations.
# First, we will get `x` as an MOI.VectorAffineFunction
x_tape = conic_form!(context, x)
ericphanson marked this conversation as resolved.
Show resolved Hide resolved
# since `ExpAtom` is restricted to `sign(x)` being real, `x_tape` is either `Vector{T}` or `SparseTape{T}`.
# The `Vector{T}` case happens when `x` is a constant (or a function of a constant).
# In this case, we can just take `exp` directly.
if x_tape isa Vector
ericphanson marked this conversation as resolved.
Show resolved Hide resolved
return exp.(x_tape)
end
return conic_form!(context, z)
vaf = to_vaf(x_tape)
# Next, we can extract the individual components of `x` via `MOI.Utilities.scalarize`
xs = MOI.Utilities.scalarize(vaf)
# Now we have a vector of `m*n` ScalarAffineFunctions in order.
# We can likewise lower `z` to a vector of `MOI.VariableIndex`
z_tape = conic_form!(context, z)
zs = z_tape.variables
for i in eachindex(xs, zs)
# Now, we wish to add the constraint `(x[i], 1, z[i]) ∈ MOI.ExponentialCone()`
# however, we have 3 different types: x[i] is a ScalarAffineFunction, 1 is a constant,
# and `z` is a VariableIndex.
# So we can't use `MOI.Utilities.vectorize`. Instead, we will construct a VectorAffineFunction manually.
# First, we construct the VectorAffineTerm's for the first and third components.
terms = [
[MOI.VectorAffineTerm(Int64(1), sat) for sat in xs[i].terms]
MOI.VectorAffineTerm(Int64(3), MOI.ScalarAffineTerm(T(1), zs[i]))
]
# Then we can add in the constants, and we are good to go.
vaf_i = MOI.VectorAffineFunction(terms, [xs[i].constant, T(1), T(0)])
MOI.add_constraint(context.model, vaf_i, MOI.ExponentialCone())
end
return z_tape
end
13 changes: 13 additions & 0 deletions src/problem_depot/problems/exp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@
@test evaluate(exp(y)) ≈ 1 atol = atol rtol = rtol
end

# Test for constant `exp` (#613)
y = Variable()
x = constant([1,2,3])
ericphanson marked this conversation as resolved.
Show resolved Hide resolved
p = minimize(sum(exp(x)) + y, y >= 0; numeric_type = T)
if test
@test problem_vexity(p) == ConvexVexity()
end
handle_problem!(p)
if test
@test p.optval ≈ sum(exp.([1,2,3])) atol = atol rtol = rtol
ericphanson marked this conversation as resolved.
Show resolved Hide resolved
@test evaluate(y) ≈ 0 atol = atol
end

y = Variable()
p = minimize(exp(y), y >= 1; numeric_type = T)

Expand Down
Loading