diff --git a/src/SemidefiniteOptInterface.jl b/src/SemidefiniteOptInterface.jl index 5d11846..69fad96 100644 --- a/src/SemidefiniteOptInterface.jl +++ b/src/SemidefiniteOptInterface.jl @@ -10,57 +10,123 @@ abstract type AbstractSDOptimizer <: MOI.AbstractOptimizer end include("interface.jl") -const VVF = MOI.VectorOfVariables -const VF = Union{MOI.SingleVariable, VVF} const SAF{T} = MOI.ScalarAffineFunction{T} -const ASF{T} = Union{MOI.SingleVariable, SAF{T}} -const ZS = Union{MOI.EqualTo, MOI.Zeros} -const NS = Union{MOI.GreaterThan, MOI.Nonnegatives} -const PS = Union{MOI.LessThan, MOI.Nonpositives} -const DS = MOI.PositiveSemidefiniteConeTriangle -const SupportedSets = Union{ZS, NS, PS, DS} +const SupportedSets = Union{MOI.Nonnegatives, MOI.PositiveSemidefiniteConeTriangle} -const VI = MOI.VariableIndex const CI{F, S} = MOI.ConstraintIndex{F, S} +const AFFEQ{T} = MOI.ConstraintIndex{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}} mutable struct SOItoMOIBridge{T, SIT <: AbstractSDOptimizer} <: MOI.AbstractOptimizer sdoptimizer::SIT - setconstant::Dict{Int64, T} - blkconstant::Dict{Int, T} + b::Vector{T} objconstant::T objsign::Int - objshift::T - nconstrs::Int - nblocks::Int blockdims::Vector{Int} - free::BitSet - varmap::Vector{Vector{Tuple{Int, Int, Int, T, T}}} # Variable Index vi -> blk, i, j, coef, shift # x = sum coef * block(X, blk)[i, j] + shift - zeroblock::Dict{CI, Int} - constrmap::Dict{CI, UnitRange{Int}} # Constraint Index ci -> cs - double::Vector{CI} # created when there are two cones for same variable + varmap::Vector{Tuple{Int, Int, Int}} # Variable Index vi -> blk, i, j function SOItoMOIBridge{T}(sdoptimizer::SIT) where {T, SIT} - new{T, SIT}(sdoptimizer, Dict{Int64, T}(), Dict{Int, T}(), - zero(T), 1, zero(T), 0, 0, - Int[], - BitSet(), - Vector{Tuple{Int, Int, Int, T}}[], - Dict{CI, Int}(), - Dict{CI, UnitRange{Int}}(), - CI[]) + new{T, SIT}(sdoptimizer, T[], zero(T), 1, + Int[], Vector{Tuple{Int, Int, Int, T}}[]) end end -varmap(optimizer::SOItoMOIBridge, vi::VI) = optimizer.varmap[vi.value] -function setvarmap!(optimizer::SOItoMOIBridge{T}, vi::VI, v::Tuple{Int, Int, Int, T, T}) where T - setvarmap!(optimizer, vi, [v]) -end -function setvarmap!(optimizer::SOItoMOIBridge{T}, vi::VI, vs::Vector{Tuple{Int, Int, Int, T, T}}) where T - optimizer.varmap[vi.value] = vs +varmap(optimizer::SOItoMOIBridge, vi::MOI.VariableIndex) = optimizer.varmap[vi.value] +function setvarmap!(optimizer::SOItoMOIBridge{T}, vi::MOI.VariableIndex, v::Tuple{Int, Int, Int}) where T + setvarmap!(optimizer, vi, v) end SDOIOptimizer(sdoptimizer::AbstractSDOptimizer, T=Float64) = SOItoMOIBridge{T}(sdoptimizer) -include("load.jl") +function MOIU.allocate(optimizer::SOItoMOIBridge, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense) + # To be sure that it is done before load(optimizer, ::ObjectiveFunction, ...), we do it in allocate + optimizer.objsign = sense == MOI.MIN_SENSE ? -1 : 1 +end +function MOIU.allocate(::SOItoMOIBridge, ::MOI.ObjectiveFunction, ::Union{MOI.SingleVariable, MOI.ScalarAffineFunction}) end + +function MOIU.load(::SOItoMOIBridge, ::MOI.ObjectiveSense, ::MOI.OptimizationSense) end +# Loads objective coefficient α * vi +function load_objective_term!(optimizer::SOItoMOIBridge, α, vi::MOI.VariableIndex) + blk, i, j = varmap(optimizer, vi) + coef = optimizer.objsign * α + if i != j + coef /= 2 + end + # in SDP format, it is max and in MPB Conic format it is min + setobjectivecoefficient!(optimizer.sdoptimizer, coef, blk, i, j) +end +function MOIU.load(optimizer::SOItoMOIBridge, ::MOI.ObjectiveFunction, f::MOI.ScalarAffineFunction) + obj = MOIU.canonical(f) + optimizer.objconstant = f.constant + for t in obj.terms + if !iszero(t.coefficient) + load_objective_term!(optimizer, t.coefficient, t.variable_index) + end + end +end +function MOIU.load(optimizer::SOItoMOIBridge{T}, ::MOI.ObjectiveFunction, f::MOI.SingleVariable) where T + load_objective_term!(optimizer, one(T), f.variable) +end + +function new_block(optimizer::SOItoMOIBridge, set::MOI.Nonnegatives) + push!(optimizer.blockdims, -MOI.dimension(set)) + blk = length(optimizer.blockdims) + for i in 1:MOI.dimension(set) + push!(optimizer.varmap, (blk, i, i)) + end +end + +function new_block(optimizer::SOItoMOIBridge, set::MOI.PositiveSemidefiniteConeTriangle) + push!(optimizer.blockdims, set.side_dimension) + blk = length(optimizer.blockdims) + for i in 1:set.side_dimension + for j in 1:i + push!(optimizer.varmap, (blk, i, j)) + end + end +end + +function MOIU.allocate_constrained_variables( + optimizer::SOItoMOIBridge, + set::Union{MOI.Nonnegatives, MOI.PositiveSemidefiniteConeTriangle} +) + offset = length(optimizer.varmap) + new_block(optimizer, set) + ci = MOI.ConstraintIndex{MOI.VectorOfVariables, typeof(set)}(offset + 1) + return [MOI.VariableIndex(i) for i in offset .+ (1:MOI.dimension(set))], ci +end + +function MOIU.load_constrained_variables( + optimizer::SOItoMOIBridge, vis::Vector{MOI.VariableIndex}, + ci::MOI.ConstraintIndex{MOI.VectorOfVariables}, + set::Union{MOI.Nonnegatives, MOI.PositiveSemidefiniteConeTriangle}) +end + +function MOIU.load_variables(optimizer::SOItoMOIBridge, nvars) + @assert nvars == length(optimizer.varmap) + init!(optimizer.sdoptimizer, optimizer.blockdims, length(optimizer.b)) +end + +function MOIU.allocate_constraint(optimizer::SOItoMOIBridge{T}, + func::MOI.ScalarAffineFunction{T}, + set::MOI.EqualTo{T}) where T + push!(optimizer.b, MOI.constant(set)) + return AFFEQ{T}(length(optimizer.b)) +end + +function MOIU.load_constraint(m::SOItoMOIBridge, ci::AFFEQ, + f::MOI.ScalarAffineFunction, s::MOI.EqualTo) + f = MOIU.canonical(f) # sum terms with same variables and same outputindex + for t in f.terms + if !iszero(t.coefficient) + blk, i, j = varmap(m, t.variable_index) + coef = t.coefficient + if i != j + coef /= 2 + end + setconstraintcoefficient!(m.sdoptimizer, coef, ci.value, blk, i, j) + end + end + setconstraintconstant!(m.sdoptimizer, MOI.constant(s) - MOI.constant(f), ci.value) +end function MOI.supports(optimizer::SOItoMOIBridge, attr::MOI.AbstractOptimizerAttribute) return MOI.supports(optimizer.sdoptimizer, attr) @@ -74,57 +140,32 @@ function MOI.set(optimizer::SOItoMOIBridge, end function MOI.is_empty(optimizer::SOItoMOIBridge) - isempty(optimizer.double) && - isempty(optimizer.setconstant) && - isempty(optimizer.blkconstant) && + isempty(optimizer.b) && iszero(optimizer.objconstant) && optimizer.objsign == 1 && - iszero(optimizer.objshift) && - iszero(optimizer.nconstrs) && - iszero(optimizer.nblocks) && isempty(optimizer.blockdims) && - isempty(optimizer.free) && - isempty(optimizer.varmap) && - isempty(optimizer.zeroblock) && - isempty(optimizer.constrmap) + isempty(optimizer.varmap) end function MOI.empty!(optimizer::SOItoMOIBridge{T}) where T - for s in optimizer.double - MOI.delete(m, s) - end MOI.empty!(optimizer.sdoptimizer) - optimizer.double = CI[] - optimizer.setconstant = Dict{Int64, T}() - optimizer.blkconstant = Dict{Int, T}() + optimizer.b = T[] optimizer.objconstant = zero(T) optimizer.objsign = 1 - optimizer.objshift = zero(T) - optimizer.nconstrs = 0 - optimizer.nblocks = 0 optimizer.blockdims = Int[] - optimizer.free = BitSet() - optimizer.varmap = Vector{Tuple{Int, Int, Int, T}}[] - optimizer.zeroblock = Dict{CI, Int}() - optimizer.constrmap = Dict{CI, UnitRange{Int}}() + optimizer.varmap = Tuple{Int, Int, Int}[] end -function setconstant!(optimizer::SOItoMOIBridge, ci::CI, s) end -function setconstant!(optimizer::SOItoMOIBridge, ci::CI, s::MOI.AbstractScalarSet) - optimizer.setconstant[ci.value] = MOI.constant(s) -end -function set_constant(optimizer::SOItoMOIBridge, - ci::CI{<:MOI.AbstractScalarFunction, - <:MOI.AbstractScalarSet}) - return optimizer.setconstant[ci.value] +function block(optimizer::SOItoMOIBridge, ci::MOI.ConstraintIndex{MOI.VectorOfVariables}) + return optimizer.varmap[ci.value][1] end -function set_constant(optimizer::SOItoMOIBridge{T}, ci::CI) where T - return zeros(T, length(optimizer.constrmap[ci])) -end -function addblkconstant(optimizer::SOItoMOIBridge, ci::CI{<:Any, <:Union{NS, PS}}, x) - blk = -ci.value - return x .+ optimizer.blkconstant[blk] +function dimension(optimizer::SOItoMOIBridge, ci::MOI.ConstraintIndex{MOI.VectorOfVariables}) + blockdim = optimizer.blockdims[block(optimizer, ci)] + if blockdim < 0 + return -blockdim + else + return MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(blockdim)) + end end -addblkconstant(optimizer::SOItoMOIBridge, ci::CI, x) = x function MOI.supports( optimizer::SOItoMOIBridge{T}, @@ -134,26 +175,12 @@ function MOI.supports( return true end -# Zeros and Nonpositives supports could be removed thanks to variable bridges -# * `VectorOfVariables`-in-`Zeros` would return a `VectorAffineFunction` with -# zero constant and no variable created. -# * `VectorOfVariables`-in-`Nonpositives` would create variables in -# `Nonnegatives` and return a `VectorAffineFunction` containing `-` the -# variables. function MOI.supports_constraint( ::SOItoMOIBridge, ::Type{MOI.VectorOfVariables}, - ::Type{<:Union{MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, + ::Type{<:Union{MOI.Nonnegatives, MOI.PositiveSemidefiniteConeTriangle}}) return true end -# This support could be remove thanks to variable bridges. -# The VectorizeVariableBridge would redirect to the above case and then the -# resulting function would be shifted by the constant. -function MOI.supports_constraint( - ::SOItoMOIBridge{T}, ::Type{MOI.SingleVariable}, - ::Type{<:Union{MOI.EqualTo{T}, MOI.GreaterThan{T}, MOI.LessThan{T}}}) where T - return true -end function MOI.supports_constraint( ::SOItoMOIBridge{T}, ::Type{MOI.ScalarAffineFunction{T}}, ::Type{MOI.EqualTo{T}}) where T @@ -170,7 +197,7 @@ MOI.optimize!(m::SOItoMOIBridge) = MOI.optimize!(m.sdoptimizer) # Objective function MOI.get(m::SOItoMOIBridge, attr::Union{MOI.ObjectiveValue, MOI.DualObjectiveValue}) - return m.objshift + m.objsign * MOI.get(m.sdoptimizer, attr) + m.objconstant + return m.objsign * MOI.get(m.sdoptimizer, attr) + m.objconstant end # Attributes @@ -182,18 +209,13 @@ end MOI.get(m::SOItoMOIBridge, ::MOI.ResultCount) = 1 -function _getblock(M, blk::Integer, s::Type{<:Union{NS, ZS}}) +function vectorize_block(M, blk::Integer, s::Type{MOI.Nonnegatives}) return diag(block(M, blk)) end -function _getblock(M, blk::Integer, s::Type{<:PS}) - return -diag(block(M, blk)) -end -# Vectorized length for matrix dimension d -sympackedlen(d::Integer) = (d*(d+1)) >> 1 -function _getblock(M::AbstractMatrix{T}, blk::Integer, s::Type{<:DS}) where T +function vectorize_block(M::AbstractMatrix{T}, blk::Integer, s::Type{MOI.PositiveSemidefiniteConeTriangle}) where T B = block(M, blk) d = LinearAlgebra.checksquare(B) - n = sympackedlen(d) + n = MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(d)) v = Vector{T}(undef, n) k = 0 for j in 1:d @@ -205,79 +227,27 @@ function _getblock(M::AbstractMatrix{T}, blk::Integer, s::Type{<:DS}) where T @assert k == n return v end -function getblock(M, blk::Integer, s::Type{<:MOI.AbstractScalarSet}) - vd = _getblock(M, blk, s) - @assert length(vd) == 1 - return vd[1] -end -function getblock(M, blk::Integer, s::Type{<:MOI.AbstractVectorSet}) - return _getblock(M, blk, s) -end -getvarprimal(m::SOItoMOIBridge, blk::Integer, S) = getblock(getX(m.sdoptimizer), blk, S) -function getvardual(m::SOItoMOIBridge, blk::Integer, S) - z = getZ(m.sdoptimizer) - b = getblock(z, blk, S) - return getblock(getZ(m.sdoptimizer), blk, S) -end - -function MOI.get(m::SOItoMOIBridge{T}, ::MOI.VariablePrimal, vi::VI) where T +function MOI.get(m::SOItoMOIBridge{T}, ::MOI.VariablePrimal, vi::MOI.VariableIndex) where T X = getX(m.sdoptimizer) - x = zero(T) - for (blk, i, j, coef, shift) in varmap(m, vi) - x += shift - if blk != 0 - x += block(X, blk)[i, j] * sign(coef) - end - end - return x -end -function MOI.get(m::SOItoMOIBridge, vp::MOI.VariablePrimal, vi::Vector{VI}) - return MOI.get.(m, vp, vi) + blk, i, j = varmap(m, vi) + return block(X, blk)[i, j] end -function _getattribute(m::SOItoMOIBridge, ci::CI{<:ASF}, f) - cs = m.constrmap[ci] - @assert length(cs) == 1 - return f(m, first(cs)) +function MOI.get(m::SOItoMOIBridge, ::MOI.ConstraintPrimal, + ci::MOI.ConstraintIndex{MOI.VectorOfVariables, S}) where S<:SupportedSets + return vectorize_block(getX(m.sdoptimizer), block(m, ci), S) end -function _getattribute(m::SOItoMOIBridge, ci::CI{<:VVF}, f) - return f.(m, m.constrmap[ci]) +function MOI.get(m::SOItoMOIBridge, ::MOI.ConstraintPrimal, ci::AFFEQ) + return m.b[ci.value] end -function MOI.get(m::SOItoMOIBridge, a::MOI.ConstraintPrimal, - ci::CI{F, S}) where {F, S} - if ci.value >= 0 - return set_constant(m, ci) - else - # Variable Function-in-S with S different from Zeros and EqualTo and not a double variable constraint - blk = -ci.value - return addblkconstant(m, ci, getvarprimal(m, blk, S)) - end -end - -function MOI.get(m::SOItoMOIBridge, ::MOI.ConstraintDual, ci::CI{<:VF, S}) where S<:SupportedSets - if ci.value < 0 - return getvardual(m, -ci.value, S) - else - dual = _getattribute(m, ci, getdual) - if haskey(m.zeroblock, ci) # ZS - return dual + getvardual(m, m.zeroblock[ci], S) - else # var constraint on unfree constraint - return dual - end - end -end - -function getdual(m::SOItoMOIBridge{T}, c::Integer) where T - if c == 0 - return zero(T) - else - return -gety(m.sdoptimizer)[c] - end +function MOI.get(m::SOItoMOIBridge, ::MOI.ConstraintDual, + ci::CI{MOI.VectorOfVariables, S}) where S<:SupportedSets + return vectorize_block(getZ(m.sdoptimizer), block(m, ci), S) end -function MOI.get(m::SOItoMOIBridge, ::MOI.ConstraintDual, ci::CI) - return _getattribute(m, ci, getdual) +function MOI.get(optimizer::SOItoMOIBridge, ::MOI.ConstraintDual, ci::AFFEQ) + return -gety(optimizer.sdoptimizer)[ci.value] end include("sdpa.jl") diff --git a/src/constraint.jl b/src/constraint.jl deleted file mode 100644 index 3c725f4..0000000 --- a/src/constraint.jl +++ /dev/null @@ -1,42 +0,0 @@ -nconstraints(f::Union{MOI.SingleVariable, SAF}, s) = 1 -nconstraints(f::VVF, s) = length(f.variables) - -function _allocate_constraint(m::SOItoMOIBridge, f, s) - ci = CI{typeof(f), typeof(s)}(m.nconstrs) - n = nconstraints(f, s) - # Fails on Julia v0.6 - #m.constrmap[ci] = m.nconstrs .+ (1:n) - m.constrmap[ci] = (m.nconstrs + 1):(m.nconstrs + n) - m.nconstrs += n - return ci -end -function MOIU.allocate_constraint(m::SOItoMOIBridge, f::SAF, s::SupportedSets) - _allocate_constraint(m::SOItoMOIBridge, f, s) -end - -function loadcoefficients!(m::SOItoMOIBridge, cs::UnitRange, - f::MOI.ScalarAffineFunction, s) - f = MOIU.canonical(f) # sum terms with same variables and same outputindex - @assert length(cs) == 1 - c = first(cs) - rhs = MOI.constant(s) - MOI.constant(f) - for t in f.terms - if !iszero(t.coefficient) - for (blk, i, j, coef, shift) in varmap(m, t.variable_index) - if !iszero(blk) - @assert !iszero(coef) - setconstraintcoefficient!(m.sdoptimizer, t.coefficient*coef, c, blk, i, j) - end - rhs -= t.coefficient * shift - end - end - end - setconstraintconstant!(m.sdoptimizer, rhs, c) -end - -function MOIU.load_constraint(m::SOItoMOIBridge, ci::CI, f::SAF, s::SupportedSets) - setconstant!(m, ci, s) - cs = m.constrmap[ci] - @assert !isempty(cs) - loadcoefficients!(m, cs, f, s) -end diff --git a/src/load.jl b/src/load.jl deleted file mode 100644 index 06ec57a..0000000 --- a/src/load.jl +++ /dev/null @@ -1,44 +0,0 @@ -include("variable.jl") -include("constraint.jl") - -function MOIU.allocate(optimizer::SOItoMOIBridge, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense) - # To be sure that it is done before load(optimizer, ::ObjectiveFunction, ...), we do it in allocate - optimizer.objsign = sense == MOI.MIN_SENSE ? -1 : 1 -end -function MOIU.allocate(::SOItoMOIBridge, ::MOI.ObjectiveFunction, ::Union{MOI.SingleVariable, MOI.ScalarAffineFunction}) end - -function MOIU.load(::SOItoMOIBridge, ::MOI.ObjectiveSense, ::MOI.OptimizationSense) end -# Loads objective coefficient α * vi -function load_objective_term!(optimizer::SOItoMOIBridge, α, vi::MOI.VariableIndex) - for (blk, i, j, coef, shift) in varmap(optimizer, vi) - if !iszero(blk) - # in SDP format, it is max and in MPB Conic format it is min - setobjectivecoefficient!(optimizer.sdoptimizer, optimizer.objsign * coef * α, blk, i, j) - end - optimizer.objshift += α * shift - end -end -function MOIU.load(optimizer::SOItoMOIBridge, ::MOI.ObjectiveFunction, f::MOI.ScalarAffineFunction) - obj = MOIU.canonical(f) - optimizer.objconstant = f.constant - for t in obj.terms - if !iszero(t.coefficient) - load_objective_term!(optimizer, t.coefficient, t.variable_index) - end - end -end -function MOIU.load(optimizer::SOItoMOIBridge{T}, ::MOI.ObjectiveFunction, f::MOI.SingleVariable) where T - load_objective_term!(optimizer, one(T), f.variable) -end - -function MOIU.allocate_variables(optimizer::SOItoMOIBridge{T}, nvars) where T - optimizer.free = BitSet(1:nvars) - optimizer.varmap = Vector{Vector{Tuple{Int, Int, Int, T, T}}}(undef, nvars) - VI.(1:nvars) -end - -function MOIU.load_variables(optimizer::SOItoMOIBridge, nvars) - @assert nvars == length(optimizer.varmap) - loadfreevariables!(optimizer) - init!(optimizer.sdoptimizer, optimizer.blockdims, optimizer.nconstrs) -end diff --git a/src/variable.jl b/src/variable.jl deleted file mode 100644 index 6be0bdf..0000000 --- a/src/variable.jl +++ /dev/null @@ -1,115 +0,0 @@ -const VIS = Union{VI, Vector{VI}} - -function newblock(m::SOItoMOIBridge, n) - push!(m.blockdims, n) - m.nblocks += 1 -end - -isfree(m, v::VI) = v.value in m.free -function unfree(m, v) - @assert isfree(m, v) - delete!(m.free, v.value) -end - -function _constraintvariable!(m::SOItoMOIBridge{T}, vs::VIS, s::ZS) where T - blk = newblock(m, -_length(vs)) - for (i, v) in _enumerate(vs) - setvarmap!(m, v, (blk, i, i, one(T), _constant(m, s))) - unfree(m, v) - end - blk -end -vscaling(::Type{<:NS}) = 1. -vscaling(::Type{<:PS}) = -1. -_length(vi::VI) = 1 -_length(vi::Vector{VI}) = length(vi) -_enumerate(vi::VI) = enumerate((vi,)) -_enumerate(vi::Vector{VI}) = enumerate(vi) -function _constraintvariable!(m::SOItoMOIBridge, vs::VIS, s::S) where S<:Union{NS, PS} - blk = newblock(m, -_length(vs)) - cst = _constant(m, s) - m.blkconstant[blk] = cst - for (i, v) in _enumerate(vs) - setvarmap!(m, v, (blk, i, i, vscaling(S), cst)) - unfree(m, v) - end - blk -end -function getmatdim(k::Integer) - # n*(n+1)/2 = k - # n^2+n-2k = 0 - # (-1 + sqrt(1 + 8k))/2 - n = div(isqrt(1 + 8k) - 1, 2) - if n * (n+1) != 2*k - error("sd dim not consistent") - end - n -end -function _constraintvariable!(m::SOItoMOIBridge{T}, vs::VIS, ::DS) where T - d = getmatdim(length(vs)) - k = 0 - blk = newblock(m, d) - for i in 1:d - for j in 1:i - k += 1 - setvarmap!(m, vs[k], (blk, i, j, i == j ? one(T) : one(T)/2, zero(T))) - unfree(m, vs[k]) - end - end - blk -end -_var(f::MOI.SingleVariable) = f.variable -_var(f::VVF) = f.variables -function _throw_error_if_unfree(m, vi::MOI.VariableIndex) - if !isfree(m, vi) - error("A variable cannot be constrained by multiple ", - "`MOI.SingleVariable` or `MOI.VectorOfVariables` constraints.") - end -end -function _throw_error_if_unfree(m, vis::MOI.Vector) - for vi in vis - _throw_error_if_unfree(m, vi) - end -end -function MOIU.allocate_constraint(m::SOItoMOIBridge{T}, f::VF, s::SupportedSets) where T - vis = _var(f) - _throw_error_if_unfree(m, vis) - blk = _constraintvariable!(m, vis, s) - if isa(s, ZS) - ci = _allocate_constraint(m, f, s) - m.zeroblock[ci] = blk - return ci - else - return CI{typeof(f), typeof(s)}(-blk) - end -end - -_constant(m::SOItoMOIBridge, s::MOI.AbstractScalarSet) = MOI.constant(s) -_constant(m::SOItoMOIBridge{T}, s::MOI.AbstractSet) where T = zero(T) - -_var(f::MOI.SingleVariable, j) = f.variable -_var(f::VVF, j) = f.variables[j] -function MOIU.load_constraint(m::SOItoMOIBridge, ci::CI, f::VF, s::SupportedSets) - if ci.value >= 0 # i.e. s is ZS or _var(f) wasn't free at allocate_constraint - setconstant!(m, ci, s) - cs = m.constrmap[ci] - @assert !isempty(cs) - for k in 1:length(cs) - vm = varmap(m, _var(f, k)) - # For free variables, the length of vm is 2, clearly not the case here - @assert length(vm) == 1 - (blk, i, j, coef, shift) = first(vm) - c = cs[k] - setconstraintcoefficient!(m.sdoptimizer, coef, c, blk, i, j) - setconstraintconstant!(m.sdoptimizer, _constant(m, s) - coef * shift, c) - end - end -end - -function loadfreevariables!(m::SOItoMOIBridge{T}) where T - for vi in m.free - blk = newblock(m, -2) - # x free transformed into x = y - z with y, z >= 0 - setvarmap!(m, VI(vi), [(blk, 1, 1, one(T), zero(T)), (blk, 2, 2, -one(T), zero(T))]) - end -end