Skip to content

Commit

Permalink
Merge pull request #89 from JuliaOpt/bl/nonnegative
Browse files Browse the repository at this point in the history
Simplify thanks to NonposToNoneg bridge
  • Loading branch information
blegat committed Feb 8, 2019
2 parents 41e14c7 + 235d273 commit 32b7b5e
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 28 deletions.
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
julia 0.6
MathProgBase 0.5 0.8
MathOptInterface 0.8.1 0.9
MathOptInterface 0.8.2 0.9
Compat 0.68
BinaryProvider 0.3
22 changes: 9 additions & 13 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const VI = MOI.VariableIndex
const MOIU = MOI.Utilities

const SF = Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64}}
const SS = Union{MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives,
MOI.SecondOrderCone, MOI.ExponentialCone}
const SS = Union{MOI.Zeros, MOI.Nonnegatives, MOI.SecondOrderCone,
MOI.ExponentialCone}

struct Solution
ret_val::Int
Expand Down Expand Up @@ -97,17 +97,15 @@ end

using Compat.SparseArrays

const LPCones = Union{MOI.Nonnegatives, MOI.Nonpositives}

# Computes cone dimensions
constroffset(cone::ConeData, ci::CI{<:MOI.AbstractFunction, MOI.Zeros}) = ci.value
function _allocate_constraint(cone::ConeData, f, s::MOI.Zeros)
ci = cone.f
cone.f += MOI.dimension(s)
ci
end
constroffset(cone::ConeData, ci::CI{<:MOI.AbstractFunction, <:LPCones}) = ci.value
function _allocate_constraint(cone::ConeData, f, s::LPCones)
constroffset(cone::ConeData, ci::CI{<:MOI.AbstractFunction, MOI.Nonnegatives}) = ci.value
function _allocate_constraint(cone::ConeData, f, s::MOI.Nonnegatives)
ci = cone.l
cone.l += MOI.dimension(s)
ci
Expand All @@ -131,8 +129,6 @@ function MOIU.allocate_constraint(instance::Optimizer, f::F, s::S) where {F <: M
end

# Build constraint matrix
scalecoef(rows, coef, minus, s) = minus ? -coef : coef
scalecoef(rows, coef, minus, s::Union{MOI.Nonpositives, Type{MOI.Nonpositives}}) = minus ? coef : -coef
output_index(t::MOI.VectorAffineTerm) = t.output_index
variable_index_value(t::MOI.ScalarAffineTerm) = t.variable_index.value
variable_index_value(t::MOI.VectorAffineTerm) = variable_index_value(t.scalar_term)
Expand All @@ -142,7 +138,7 @@ constrrows(s::MOI.AbstractVectorSet) = 1:MOI.dimension(s)
constrrows(instance::Optimizer, ci::CI{<:MOI.AbstractVectorFunction, MOI.Zeros}) = 1:instance.cone.eqnrows[constroffset(instance, ci)]
constrrows(instance::Optimizer, ci::CI{<:MOI.AbstractVectorFunction, <:MOI.AbstractVectorSet}) = 1:instance.cone.ineqnrows[constroffset(instance, ci)]
matrix(data::ModelData, s::MOI.Zeros) = data.b, data.IA, data.JA, data.VA
matrix(data::ModelData, s::Union{LPCones, MOI.SecondOrderCone, MOI.ExponentialCone}) = data.h, data.IG, data.JG, data.VG
matrix(data::ModelData, s::Union{MOI.Nonnegatives, MOI.SecondOrderCone, MOI.ExponentialCone}) = data.h, data.IG, data.JG, data.VG
matrix(instance::Optimizer, s) = matrix(instance.data, s)
MOIU.load_constraint(instance::Optimizer, ci, f::MOI.VectorOfVariables, s) = MOIU.load_constraint(instance, ci, MOI.VectorAffineFunction{Float64}(f), s)
# ECOS orders differently than MOI the second and third dimension of the exponential cone
Expand Down Expand Up @@ -171,10 +167,10 @@ function MOIU.load_constraint(instance::Optimizer, ci, f::MOI.VectorAffineFuncti
# The ECOS format is b - Ax ∈ cone
# so minus=false for b and minus=true for A
b, Is, Js, Vs = matrix(instance, s)
b[i] .= scalecoef(rows, orderval(f.constants, s), false, s)
b[i] .= orderval(f.constants, s)
append!(Is, offset .+ orderidx(I, s))
append!(Js, J)
append!(Vs, scalecoef(I, V, true, s))
append!(Vs, -V)
end

function MOIU.allocate_variables(instance::Optimizer, nvars::Integer)
Expand Down Expand Up @@ -318,7 +314,7 @@ end
function MOI.get(instance::Optimizer, ::MOI.ConstraintPrimal, ci::CI{<:MOI.AbstractFunction, S}) where S <: MOI.AbstractSet
offset = constroffset(instance, ci)
rows = constrrows(instance, ci)
return scalecoef(rows, reorderval(instance.sol.slack[offset .+ rows], S), false, S)
return reorderval(instance.sol.slack[offset .+ rows], S)
end

function MOI.get(instance::Optimizer, ::MOI.DualStatus)
Expand Down Expand Up @@ -346,7 +342,7 @@ _dual(instance, ci::CI) = instance.sol.dual_ineq
function MOI.get(instance::Optimizer, ::MOI.ConstraintDual, ci::CI{<:MOI.AbstractFunction, S}) where S <: MOI.AbstractSet
offset = constroffset(instance, ci)
rows = constrrows(instance, ci)
scalecoef(rows, reorderval(_dual(instance, ci)[offset .+ rows], S), false, S)
return reorderval(_dual(instance, ci)[offset .+ rows], S)
end

MOI.get(instance::Optimizer, ::MOI.ResultCount) = 1
39 changes: 25 additions & 14 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
using Compat
using Compat.Test

using MathOptInterface
const MOI = MathOptInterface
const MOIT = MOI.Test
const MOIU = MOI.Utilities
const MOIB = MOI.Bridges

const MOIU = MOI.Utilities
import ECOS
const optimizer = ECOS.Optimizer(verbose=false)

@testset "SolverName" begin
@test MOI.get(optimizer, MOI.SolverName()) == "ECOS"
end

@testset "supports_allocate_load" begin
@test MOIU.supports_allocate_load(optimizer, false)
@test !MOIU.supports_allocate_load(optimizer, true)
end

MOIU.@model(ECOSModelData,
(), (), # No scalar functions
(MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, MOI.SecondOrderCone,
Expand All @@ -12,13 +27,18 @@ MOIU.@model(ECOSModelData,
(), (), # No scalar sets
(MOI.VectorOfVariables,),
(MOI.VectorAffineFunction,))
const optimizer = MOIU.CachingOptimizer(ECOSModelData{Float64}(), ECOS.Optimizer(verbose=false))
# UniversalFallback is needed for starting values, even if they are ignored by ECOS
const cache = MOIU.UniversalFallback(ECOSModelData{Float64}())
const cached = MOIU.CachingOptimizer(cache, optimizer)

# Essential bridges that are needed for all tests
const bridged = MOIB.Vectorize{Float64}(MOIB.NonposToNonneg{Float64}(cached))

# SOC2 requires 1e-4
const config = MOIT.TestConfig(atol=1e-4, rtol=1e-4)

@testset "Unit" begin
MOIT.unittest(MOIB.SplitInterval{Float64}(MOIB.Vectorize{Float64}(optimizer)),
MOIT.unittest(MOIB.SplitInterval{Float64}(bridged),
config,
[# Quadratic functions are not supported
"solve_qcp_edge_cases", "solve_qp_edge_cases",
Expand All @@ -27,21 +47,12 @@ const config = MOIT.TestConfig(atol=1e-4, rtol=1e-4)
end

@testset "Continuous linear problems" begin
MOIT.contlineartest(MOIB.SplitInterval{Float64}(MOIB.Vectorize{Float64}(optimizer)),
MOIT.contlineartest(MOIB.SplitInterval{Float64}(bridged),
config)
end

@testset "Continuous conic problems" begin
exclude = ["sdp", "rootdet", "logdet"]
MOIT.contconictest(MOIB.GeoMean{Float64}(MOIB.RSOC{Float64}(MOIB.Vectorize{Float64}(optimizer))),
MOIT.contconictest(MOIB.GeoMean{Float64}(MOIB.RSOC{Float64}(bridged)),
config, exclude)
end

@testset "SolverName" begin
@test MOI.get(optimizer, MOI.SolverName()) == "ECOS"
end

@testset "supports_allocate_load" begin
@test MOIU.supports_allocate_load(optimizer.optimizer, false)
@test !MOIU.supports_allocate_load(optimizer.optimizer, true)
end

0 comments on commit 32b7b5e

Please sign in to comment.