Skip to content

Implement starting values for NormOneBridge #994

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

Merged
merged 1 commit into from
Jan 5, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
61 changes: 52 additions & 9 deletions src/Bridges/Constraint/norm_to_lp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,64 @@ function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormOneBridge)
dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2)
return MOI.NormOneCone(dim)
end
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormOneBridge)
ge_primal = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index)
nn_primal = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index)

function MOI.supports(
::MOI.ModelLike,
::Union{MOI.ConstraintPrimalStart, MOI.ConstraintDualStart},
::Type{<:NormOneBridge})

return true
end
function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart,
bridge::NormOneBridge{T}, value) where T
x_value = value[1 .+ (1:length(bridge.y))]
y_value = abs.(x_value)
for i in eachindex(bridge.y)
MOI.set(model, MOI.VariablePrimalStart(), bridge.y[i], y_value[i])
end
MOI.set(model, attr, bridge.nn_index, [y_value - x_value; y_value + x_value])
MOI.set(model, attr, bridge.ge_index, value[1] - reduce(+, y_value, init=zero(T)))
return
end
function MOI.get(model::MOI.ModelLike,
attr::Union{MOI.ConstraintPrimal, MOI.ConstraintPrimalStart},
bridge::NormOneBridge)
ge_primal = MOI.get(model, attr, bridge.ge_index)
nn_primal = MOI.get(model, attr, bridge.nn_index)
t = ge_primal + sum(nn_primal) / 2
d = length(c.y)
d = length(bridge.y)
x = (nn_primal[(d + 1):end] - nn_primal[1:d]) / 2
return vcat(t, x)
end
# Given a_i is dual on y_i - x_i >= 0 and b_i is dual on y_i + x_i >= 0 and c is dual on t - sum(y) >= 0,
# the dual on (t, x) in NormOneCone is (u, v) in NormInfinityCone, where
# v_i = -a_i + b_i and u = c.
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormOneBridge)
t = MOI.get(model, MOI.ConstraintDual(), c.ge_index)
nn_dual = MOI.get(model, MOI.ConstraintDual(), c.nn_index)
d = div(length(nn_dual), 2)
x = (nn_dual[(d + 1):end] - nn_dual[1:d])
function MOI.get(model::MOI.ModelLike,
attr::Union{MOI.ConstraintDual, MOI.ConstraintDualStart},
bridge::NormOneBridge)
t = MOI.get(model, attr, bridge.ge_index)
nn_dual = MOI.get(model, attr, bridge.nn_index)
d = length(bridge.y)
x = nn_dual[(d + 1):end] - nn_dual[1:d]
return vcat(t, x)
end
# value[1 + i] = nn_dual[d + i] - nn_dual[i]
# and `nn_dual` is nonnegative. By complementarity slackness, only one of each
# `nn_dual` can be nonzero (except if `x = 0`) so we can set
# depending on the sense of `value[1 + i]`.
function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintDualStart,
bridge::NormOneBridge, value)
t = MOI.set(model, MOI.ConstraintDualStart(), bridge.ge_index, value[1])
d = length(bridge.y)
nn_dual = zeros(eltype(value), 2d)
for i in eachindex(bridge.y)
v = value[1 + i]
if v < 0
nn_dual[i] = -v
else
nn_dual[d + i] = v
end
end
MOI.set(model, MOI.ConstraintDualStart(), bridge.nn_index, nn_dual)
return
end
33 changes: 26 additions & 7 deletions test/Bridges/Constraint/norm_to_lp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,17 @@ end
MOIT.normone1vtest(bridged_mock, config)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a question: what's the point of running this test set here? it's not relevant to all the below tests right? I ask because in #976 I have multiple test sets but currently only run one of them in the bridge test file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not affect tests below but it still adds coverage. normone1f uses VAF functions and normone1v uses VOV. So this tests that the bridging and the getters of ConstraintPrimal and ConstrainDual work when the input function is VOV

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I didn't realize it wasn't called elsewhere. I'll add both tests for spectral and for nuclear cones then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait no I misunderstood. it only works for the same test modulo the vov/vaf difference. so scrap what I said.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so in your case you'll have to redo the mock dual and primal values so probably not worth it

MOIT.normone1ftest(bridged_mock, config)

var_names = ["x", "y", "z"]
MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names)

nonneg = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives}())
greater = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}}())
u, v = MOI.get(mock, MOI.ListOfVariableIndices())[4:5]
@testset "Test mock model" begin
var_names = ["x", "y", "z", "u", "v"]
MOI.set(mock, MOI.VariableName(), MOI.get(mock, MOI.ListOfVariableIndices()), var_names)
nonneg = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives}())
MOI.set(mock, MOI.VariableName(), u, "u")
MOI.set(mock, MOI.VariableName(), v, "v")
@test length(nonneg) == 1
MOI.set(mock, MOI.ConstraintName(), nonneg[1], "nonneg")
greater = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}}())
@test length(greater) == 1
MOI.set(mock, MOI.ConstraintName(), greater[1], "greater")
zeros = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Zeros}())
Expand All @@ -124,12 +128,10 @@ end
"""
model = MOIU.Model{Float64}()
MOIU.loadfromstring!(model, s)
MOIU.test_models_equal(mock, model, var_names, ["nonneg", "greater", "x_eq", "y_eq"])
MOIU.test_models_equal(mock, model, [var_names; "u"; "v"], ["nonneg", "greater", "x_eq", "y_eq"])
end

@testset "Test bridged model" begin
var_names = ["x", "y", "z"]
MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names)
normone = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}())
@test length(normone) == 1
MOI.set(bridged_mock, MOI.ConstraintName(), normone[1], "normone")
Expand All @@ -151,6 +153,23 @@ end
end

ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}()))

@testset "$attr" for attr in [MOI.ConstraintPrimalStart(), MOI.ConstraintDualStart()]
@test MOI.supports(bridged_mock, attr, typeof(ci))
value = [4.0, 1.0, -2.0]
MOI.set(bridged_mock, attr, ci, value)
@test MOI.get(bridged_mock, attr, ci) ≈ value
if attr isa MOI.ConstraintPrimalStart
@test MOI.get(mock, MOI.VariablePrimalStart(), u) == 1
@test MOI.get(mock, MOI.VariablePrimalStart(), v) == 2
@test MOI.get(mock, attr, nonneg[1]) == [0.0, 4.0, 2.0, 0.0]
@test MOI.get(mock, attr, greater[1]) == 1
else
@test MOI.get(mock, attr, nonneg[1]) == [0.0, 2.0, 1.0, 0.0]
@test MOI.get(mock, attr, greater[1]) == 4
end
end

test_delete_bridge(bridged_mock, ci, 3, (
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0),
(MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0)))
Expand Down