JuliaPolyhedra / Polyhedra.jl

Polyhedral Computation Interface
Other
172 stars 27 forks source link

[feature] Support MOI.VectorAffineFunction #170

Open oyamad opened 5 years ago

oyamad commented 5 years ago

Would it be costly to support MOI.VectorAffineFunction also? https://github.com/JuliaPolyhedra/Polyhedra.jl/blob/3b289ddf7ab4c8626c87eaf8a42f0e73cc2d7e26/src/lphrep.jl#L3-L5

blegat commented 5 years ago

It is already supported through bridges: https://github.com/JuliaPolyhedra/Polyhedra.jl/blob/3b289ddf7ab4c8626c87eaf8a42f0e73cc2d7e26/src/lphrep.jl#L21 full_bridge_optimizer adds many bridges including http://www.juliaopt.org/MathOptInterface.jl/dev/apireference/#MathOptInterface.Bridges.ScalarizeBridge which transforms VectorAffineFunction to ScalarAffineFunction.

oyamad commented 5 years ago

@blegat Thanks. My problem seems to be that I haven't been able to understand the manual of MOI...

To be concrete, consider the linear program max c x s.t. A x <= b, where

A = [1 1; -1 0; 0 -1]
b = [1, 0, 0]
c = [1, 0]

Let's use CDDLib.Optimizer:

T = Float64  # or Rational{BigInt}
optimizer = CDDLib.Optimizer{T}()
x = MOI.add_variables(optimizer, length(c))
MOI.set(optimizer, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction{T}(MOI.ScalarAffineTerm{T}.(c, x), 0))
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)

I know the following works:

for i in 1:size(A, 1)
    MOI.add_constraint(
        optimizer,
        MOI.ScalarAffineFunction{T}(
            MOI.ScalarAffineTerm{T}.(A[i, :], x), 0
        ),
        MOI.LessThan{T}(b[i])
    )
end
MOI.optimize!(optimizer)
2-element Array{Float64,1}:
 1.0
 0.0

But it feels a bit stupid to pass the content of the matrix A row by row, so I wanted to pass A itself (along with b) without decomposing it. Is it possible? The following doesn't work:

MOI.add_constraint(
    optimizer,
    MOI.VectorAffineFunction{T}(A, -b),
    MOI.Nonpositives(length(b))
)
MethodError: no method matching Array{MathOptInterface.VectorAffineTerm{Float64},1}(::Array{Int64,2})
blegat commented 5 years ago

See this discourse post for how to formulate the constraint with VectorAffineFunction.

oyamad commented 5 years ago

Ah sorry, I should have read that thread carefully.

Now the issue seems to be that MOI.Nonpositives is not supported:

T = Float64  # or Rational{BigInt}
optimizer = CDDLib.Optimizer{T}()
x = MOI.add_variables(optimizer, length(c))
MOI.set(optimizer, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction{T}(MOI.ScalarAffineTerm{T}.(c, x), 0))
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)

terms = MOI.VectorAffineTerm{T}.(
    1:size(A, 1), MOI.ScalarAffineTerm{T}.(A, reshape(x, 1, length(x)))
)
f = MOI.VectorAffineFunction{T}(vec(terms), -b)
MOI.add_constraint(optimizer, f, MOI.Nonpositives(size(A, 1)))
MathOptInterface.UnsupportedConstraint{MathOptInterface.VectorAffineFunction{Float64},MathOptInterface.Nonpositives}: `MathOptInterface.VectorAffineFunction{Float64}`-in-`MathOptInterface.Nonpositives` constraints is not supported by the model.
blegat commented 5 years ago

You should do optimizer = MOI.Bridges.full_bridge_optimiezr(CDDLib.Optimizer{T}(), T)

oyamad commented 5 years ago

Great, that works now.

In PR #171 I added a description of the usage to the documentation.