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

MOI.NormOneCone errors #220

Closed
AlexRobson opened this issue Apr 27, 2022 · 4 comments · Fixed by #219
Closed

MOI.NormOneCone errors #220

AlexRobson opened this issue Apr 27, 2022 · 4 comments · Fixed by #219

Comments

@AlexRobson
Copy link
Contributor

AlexRobson commented Apr 27, 2022

I was hitting an error when including a norm one constraint and needed to reformulation to get it to work. These two formalations should afaik be equivalent ( \sum{|z_i|} < h)
The forward pass is fine, but on running reverse it hits a ERROR: InexactError: convert(MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.ScalarQuadraticFunction{Float64}... error.

Related to #202

If I've diagnosed the issue correclty, my guess from that issue is that a MOI bridge is missing? I don't know enough on the internals though to suggest how to solve it though :)

using DiffOpt
using ECOS
using JuMP
using Test

function qp_1(q::AbstractVector, Q::AbstractMatrix, h)

    m = Model(() -> DiffOpt.diff_optimizer(ECOS.Optimizer))
    set_silent(m)
    Ydim = length(q)

    @variable(m, z[1:Ydim]) 

    # Add a NormOne Constraint
    @variable(m, t[1:Ydim])
    @constraint(m, t .>= z)
    @constraint(m, t .>= -z)
    @constraint(m, sum(t) <= h)

    @objective(m, Min, 0.5 * z'*Q*z + q'z)
    optimize!(m)
    return m
end

function qp_2(q::AbstractVector, Q::AbstractMatrix, h)

    m = Model(() -> DiffOpt.diff_optimizer(ECOS.Optimizer))
    set_silent(m)

    Ydim = length(q)

    @variable(m, z[1:Ydim]) 

    # Add a NormOne Constraint
    @constraint(m, [h; z] in MOI.NormOneCone(Ydim + 1))

    @objective(m, Min, 0.5 * z'*Q*z + q'z)
    optimize!(m)
    return m
end

r1 = rand(5,5);
q = rand(5,);
Q = r1' * r1;
z̄ = rand(5,)

m1 = qp_1(q, Q, 0.5)
m2 = qp_2(q, Q, 0.5)
@test MOI.get.(m1, MOI.VariablePrimal(), m1[:z]) ≈ MOI.get.(m2, MOI.VariablePrimal(), m2[:z])

MOI.set.(m1, DiffOpt.BackwardInVariablePrimal(), m1[:z], z̄)
MOI.set.(m2, DiffOpt.BackwardInVariablePrimal(), m2[:z], z̄)

DiffOpt.backward(m1) # Suceeds 
DiffOpt.backward(m2) # Fails 

# InexactError: convert(MathOptInterface.ScalarAffineFunction{Float64} ...
julia> DiffOpt.backward(m2) # Fails
ERROR: InexactError: convert(MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.ScalarQuadraticFunction{Float64}(MathOptInterface.ScalarQuadraticTerm{Float64}[MathOptInterface.ScalarQuadraticTerm{Float64}(1.9017060390833442, MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarQuadraticTerm{Float64}(2.2390862422373563, MathOptInterface.VariableIndex(2), MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarQuadraticTerm{Float64}<snip repeated>MathOptInterface.VariableIndex(4)), MathOptInterface.ScalarAffineTerm{Float64}(0.3884591773383528, MathOptInterface.VariableIndex(5))], 0.0))
Stacktrace:
  [1] convert
    @ ~/.julia/packages/MathOptInterface/ajp5T/src/functions.jl:549 [inlined]
  [2] get(o::MathOptInterface.Utilities.ObjectiveContainer{Float64}, #unused#::MathOptInterface.ObjectiveFunction{MathOptInterface.ScalarAffineFunction{Float64}})
    @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/ajp5T/src/Utilities/objective_container.jl:100
  [3] get
    @ ~/.julia/packages/MathOptInterface/ajp5T/src/Utilities/model.jl:294 [inlined]
  [4] get
    @ ~/.julia/packages/DiffOpt/guTOh/src/diff_opt.jl:436 [inlined]
  [5] _gradient_cache(model::DiffOpt.ConicDiff)
    @ DiffOpt ~/.julia/packages/DiffOpt/guTOh/src/conic_diff.jl:89
  [6] backward(model::DiffOpt.ConicDiff)
    @ DiffOpt ~/.julia/packages/DiffOpt/guTOh/src/conic_diff.jl:219
  [7] backward
    @ ~/.julia/packages/DiffOpt/guTOh/src/jump_moi_overloads.jl:276 [inlined]
  [8] backward(model::DiffOpt.Optimizer{MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{ECOS.Optimizer, MathOptInterface.Utilities.UniversalFallback{ECOS.OptimizerCache}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}})
    @ DiffOpt ~/.julia/packages/DiffOpt/guTOh/src/moi_wrapper.jl:490
  [9] backward(model::MathOptInterface.Bridges.LazyBridgeOptimizer{DiffOpt.Optimizer{MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{ECOS.Optimizer, MathOptInterface.Utilities.UniversalFallback{ECOS.OptimizerCache}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}})
    @ DiffOpt ~/.julia/packages/DiffOpt/guTOh/src/jump_moi_overloads.jl:276
 [10] backward(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{DiffOpt.Optimizer{MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{ECOS.Optimizer, MathOptInterface.Utilities.UniversalFallback{ECOS.OptimizerCache}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}})
    @ DiffOpt ~/.julia/packages/DiffOpt/guTOh/src/jump_moi_overloads.jl:272
 [11] backward(model::Model)
    @ DiffOpt ~/.julia/packages/DiffOpt/guTOh/src/jump_moi_overloads.jl:268
 [12] top-level scope
@matbesancon
Copy link
Collaborator

thanks for reporting, can you post the full error message?

@matbesancon
Copy link
Collaborator

@blegat it seems we allow setting a quadratic objective for the conic model even though we shouldn't, we then force it into a scalar affine function which fails

ERROR: InexactError: convert(MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.ScalarQuadraticFunction{Float64}(MathOptInterface.ScalarQuadraticTerm{Float64}[MathOptInterface.ScalarQuadraticTerm{Float64}(1.237499608762795, MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarQuadraticTerm{Float64}(0.5835543737796739, MathOptInterface.VariableIndex(2), MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarQuadraticTerm{Float64}(0.4897992791708482, MathOptInterface.VariableIndex(3), MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarQuadraticTerm{Float64}(0.939956265309776, MathOptInterface.VariableIndex(4), MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarQuadraticTerm{Float64}(0.8050602396420274, MathOptInterface.VariableIndex(5), MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarQuadraticTerm{Float64}(1.2270184161152333, MathOptInterface.VariableIndex(2), MathOptInterface.VariableIndex(2)), MathOptInterface.ScalarQuadraticTerm{Float64}(0.6716984665984245, MathOptInterface.VariableIndex(3), MathOptInterface.VariableIndex(2)), MathOptInterface.ScalarQuadraticTerm{Float64}(1.0660539177226287, MathOptInterface.VariableIndex(4), MathOptInterface.VariableIndex(2)), MathOptInterface.ScalarQuadraticTerm{Float64}(1.158530646143926, MathOptInterface.VariableIndex(5), MathOptInterface.VariableIndex(2)), MathOptInterface.ScalarQuadraticTerm{Float64}(1.17618529994468, MathOptInterface.VariableIndex(3), MathOptInterface.VariableIndex(3)), MathOptInterface.ScalarQuadraticTerm{Float64}(0.722027660522077, MathOptInterface.VariableIndex(4), MathOptInterface.VariableIndex(3)), MathOptInterface.ScalarQuadraticTerm{Float64}(1.426803494178496, MathOptInterface.VariableIndex(5), MathOptInterface.VariableIndex(3)), MathOptInterface.ScalarQuadraticTerm{Float64}(1.3396775261990599, MathOptInterface.VariableIndex(4), MathOptInterface.VariableIndex(4)), MathOptInterface.ScalarQuadraticTerm{Float64}(1.149342511187884, MathOptInterface.VariableIndex(5), MathOptInterface.VariableIndex(4)), MathOptInterface.ScalarQuadraticTerm{Float64}(2.5208016412096113, MathOptInterface.VariableIndex(5), MathOptInterface.VariableIndex(5))], MathOptInterface.ScalarAffineTerm{Float64}[MathOptInterface.ScalarAffineTerm{Float64}(0.29446130075204435, MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarAffineTerm{Float64}(0.6279540954624389, MathOptInterface.VariableIndex(2)), MathOptInterface.ScalarAffineTerm{Float64}(0.3761417578264732, MathOptInterface.VariableIndex(3)), MathOptInterface.ScalarAffineTerm{Float64}(0.22401733841074956, MathOptInterface.VariableIndex(4)), MathOptInterface.ScalarAffineTerm{Float64}(0.8584856063636308, MathOptInterface.VariableIndex(5))], 0.0))
Stacktrace:
  [1] convert
    @ ~/.julia/packages/MathOptInterface/ajp5T/src/functions.jl:549 [inlined]
  [2] get(o::MathOptInterface.Utilities.ObjectiveContainer{Float64}, #unused#::MathOptInterface.ObjectiveFunction{MathOptInterface.ScalarAffineFunction{Float64}})
    @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/ajp5T/src/Utilities/objective_container.jl:100
  [3] get
    @ ~/.julia/packages/MathOptInterface/ajp5T/src/Utilities/model.jl:294 [inlined]
  [4] get
    @ ~/julia_projects/DiffOpt.jl/src/diff_opt.jl:434 [inlined]
  [5] _gradient_cache(model::DiffOpt.ConicDiffProblem)
    @ DiffOpt ~/julia_projects/DiffOpt.jl/src/conic_diff.jl:89
  [6] reverse_differentiate!(model::DiffOpt.ConicDiffProblem)
    @ DiffOpt ~/julia_projects/DiffOpt.jl/src/conic_diff.jl:217
  [7] reverse_differentiate!
    @ ~/julia_projects/DiffOpt.jl/src/jump_moi_overloads.jl:276 [inlined]
  [8] reverse_differentiate!(model::DiffOpt.Optimizer{MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{ECOS.Optimizer, MathOptInterface.Utilities.UniversalFallback{ECOS.OptimizerCache}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}})
    @ DiffOpt ~/julia_projects/DiffOpt.jl/src/moi_wrapper.jl:485
  [9] reverse_differentiate!(model::MathOptInterface.Bridges.LazyBridgeOptimizer{DiffOpt.Optimizer{MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{ECOS.Optimizer, MathOptInterface.Utilities.UniversalFallback{ECOS.OptimizerCache}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}})
    @ DiffOpt ~/julia_projects/DiffOpt.jl/src/jump_moi_overloads.jl:276
 [10] reverse_differentiate!(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{DiffOpt.Optimizer{MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{ECOS.Optimizer, MathOptInterface.Utilities.UniversalFallback{ECOS.OptimizerCache}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}})
    @ DiffOpt ~/julia_projects/DiffOpt.jl/src/jump_moi_overloads.jl:272
 [11] reverse_differentiate!(model::Model)
    @ DiffOpt ~/julia_projects/DiffOpt.jl/src/jump_moi_overloads.jl:268

@AlexRobson
Copy link
Contributor Author

AlexRobson commented Apr 27, 2022

thanks for reporting, can you post the full error message?

Ah, sorry - I've now included the error - Although looks like you beat me to it :)

@blegat
Copy link
Member

blegat commented Apr 29, 2022

The issue is that the NormOneCone can be bridged to an LP so the quadratic programming model can be used but _qp_supported does not see it.
#219 should solve the issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

3 participants