From 7e058fc7c2a93ccf0632bd085049341c02a02895 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 11 May 2021 15:30:43 +1200 Subject: [PATCH 1/6] [breaking] Modify the dimension of Complements --- NEWS.md | 5 ++++- src/FileFormats/MOF/read.jl | 2 +- src/FileFormats/MOF/write.jl | 8 +------ src/Test/UnitTests/basic_constraint_tests.jl | 2 +- src/Test/nlp.jl | 4 ++-- src/sets.jl | 23 ++++++++++++++------ test/sets.jl | 4 +++- 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/NEWS.md b/NEWS.md index e1c99cac0e..1d426b0f20 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,7 +6,7 @@ v0.10.0 (In development) **This is a breaking release.** -Breaking renames: +Breaking renames: - `ListOfConstraints` has been renamed to `ListOfConstraintTypesPresent` - `SolveTime` has been renamed to `SolveTimeSec` @@ -14,6 +14,9 @@ Breaking renames: - The `.variable_index` field in `ScalarAffineTerm` has been renamed to `.variable` - The `.variable_index_1` field in `ScalarQuadraticTerm` has been renamed to `.variable_1` - The `.variable_index_2` field in `ScalarQuadraticTerm` has been renamed to `.variable_2` +- The `dimension` argument to `Complements(dimension::Int)` should now be the + length of the corresponding function, instead of half the length. An + `ArgumentError` is thrown if `dimension` is not even. - `Constraint.RSOCBridge` has been renamed to `Constraint.RSOCtoSOCBridge` - `Constraint.SOCRBridge` has been renamed to `Constraint.SOCtoRSOCBridge` diff --git a/src/FileFormats/MOF/read.jl b/src/FileFormats/MOF/read.jl index 0bbe4a7c50..43463b86cc 100644 --- a/src/FileFormats/MOF/read.jl +++ b/src/FileFormats/MOF/read.jl @@ -452,7 +452,7 @@ function set_to_moi(::Val{:DualExponentialCone}, ::Object) end function set_to_moi(::Val{:Complements}, object::Object) - return MOI.Complements(div(object["dimension"], 2)) + return MOI.Complements(object["dimension"]) end # ========== Typed vector sets ========== diff --git a/src/FileFormats/MOF/write.jl b/src/FileFormats/MOF/write.jl index 2c3b893e53..2df535f668 100644 --- a/src/FileFormats/MOF/write.jl +++ b/src/FileFormats/MOF/write.jl @@ -284,6 +284,7 @@ end function head_name(::Type{MOI.PositiveSemidefiniteConeSquare}) return "PositiveSemidefiniteConeSquare" end +head_name(::Type{MOI.Complements}) = "Complements" # ========== Typed vector sets ========== head_name(::Type{<:MOI.PowerCone}) = "PowerCone" @@ -302,10 +303,3 @@ function moi_to_object( "activate_on" => (I == MOI.ACTIVATE_ON_ONE) ? "one" : "zero", ) end - -function moi_to_object(set::MOI.Complements, ::Dict{MOI.VariableIndex,String}) - return OrderedObject( - "type" => "Complements", - "dimension" => MOI.dimension(set), - ) -end diff --git a/src/Test/UnitTests/basic_constraint_tests.jl b/src/Test/UnitTests/basic_constraint_tests.jl index 67963c6a54..6cc186dbbe 100644 --- a/src/Test/UnitTests/basic_constraint_tests.jl +++ b/src/Test/UnitTests/basic_constraint_tests.jl @@ -123,7 +123,7 @@ const BasicConstraintTests = Dict( (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => (dummy_vector_affine, 2, MOI.Nonnegatives(2)), (MOI.VectorAffineFunction{Float64}, MOI.Complements) => - (dummy_vector_affine, 2, MOI.Complements(1)), + (dummy_vector_affine, 2, MOI.Complements(2)), (MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) => (dummy_vector_affine, 3, MOI.NormInfinityCone(3)), (MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) => diff --git a/src/Test/nlp.jl b/src/Test/nlp.jl index f29118b521..701f864f14 100644 --- a/src/Test/nlp.jl +++ b/src/Test/nlp.jl @@ -527,7 +527,7 @@ function test_linear_mixed_complementarity( MOI.add_constraint( model, MOI.VectorAffineFunction(terms, [q; 0.0; 0.0; 0.0; 0.0]), - MOI.Complements(4), + MOI.Complements(8), ) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED if config.solve @@ -631,7 +631,7 @@ function test_qp_complementarity_constraint( MOI.add_constraint( model, MOI.VectorOfVariables([x[3], x[4], x[5], x[6], x[7], x[8]]), - MOI.Complements(3), + MOI.Complements(6), ) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED if config.solve diff --git a/src/sets.jl b/src/sets.jl index 2b8772d500..e71a4e3819 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -806,10 +806,11 @@ The set corresponding to a mixed complementarity constraint. Complementarity constraints should be specified with an [`AbstractVectorFunction`](@ref)-in-`Complements(dimension)` constraint. -The dimension of the vector-valued function `F` must be `2 * dimension`. This +The dimension of the vector-valued function `F` must be `dimension`. This defines a complementarity constraint between the scalar function `F[i]` and the -variable in `F[i + dimension]`. Thus, `F[i + dimension]` must be interpretable -as a single variable `x_i` (e.g., `1.0 * x + 0.0`). +variable in `F[i + dimension/2]`. Thus, `F[i + dimension/2]` must be +interpretable as a single variable `x_i` (e.g., `1.0 * x + 0.0`), and +`dimension` must be even. The mixed complementarity problem consists of finding `x_i` in the interval `[lb, ub]` (i.e., in the set `Interval(lb, ub)`), such that the following holds: @@ -826,7 +827,7 @@ Classically, the bounding set for `x_i` is `Interval(0, Inf)`, which recovers: The problem: x -in- Interval(-1, 1) - [-4 * x - 3, x] -in- Complements(1) + [-4 * x - 3, x] -in- Complements(2) defines the mixed complementarity problem where the following holds: @@ -844,17 +845,25 @@ The function `F` can also be defined in terms of single variables. For example, the problem: [x_3, x_4] -in- Nonnegatives(2) - [x_1, x_2, x_3, x_4] -in- Complements(2) + [x_1, x_2, x_3, x_4] -in- Complements(4) defines the complementarity problem where `0 <= x_1 ⟂ x_3 >= 0` and `0 <= x_2 ⟂ x_4 >= 0`. """ struct Complements <: AbstractVectorSet dimension::Int + function Complements(dimension::Int) + if !iseven(dimension) + throw( + ArgumentError( + "The dimension of a Complements set must be even.", + ), + ) + end + return new(dimension) + end end -dimension(set::Complements) = 2 * set.dimension - # isbits types, nothing to copy function Base.copy( set::Union{ diff --git a/test/sets.jl b/test/sets.jl index a568cfb439..b2eacc8164 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -106,7 +106,9 @@ Base.copy(mlt::MutLessThan) = MutLessThan(Base.copy(mlt.upper)) ) == 2 @test MOI.dimension(MOI.Complements(5)) == 10 end - + @testset "Complements" begin + @test_throws ArgumentError MOI.Complements(3) + end @testset "Dual Set" begin function dual_set_test(set1, set2) @test MOI.dual_set(set1) == set2 From 8ac33937cb70c8a4495810790d3ce15fbda95fef Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 11 May 2021 15:36:09 +1200 Subject: [PATCH 2/6] Missed one --- test/sets.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sets.jl b/test/sets.jl index b2eacc8164..796efef3ea 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -104,7 +104,7 @@ Base.copy(mlt::MutLessThan) = MutLessThan(Base.copy(mlt.upper)) @test MOI.dimension( MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE}(MOI.LessThan(1.0)), ) == 2 - @test MOI.dimension(MOI.Complements(5)) == 10 + @test MOI.dimension(MOI.Complements(10)) == 10 end @testset "Complements" begin @test_throws ArgumentError MOI.Complements(3) From a8f97d8a36921fe60eb292793c00ae70f16d179a Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 11 May 2021 16:06:31 +1200 Subject: [PATCH 3/6] Update MOF.jl --- test/FileFormats/MOF/MOF.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/FileFormats/MOF/MOF.jl b/test/FileFormats/MOF/MOF.jl index 072e284b70..25f4c1d874 100644 --- a/test/FileFormats/MOF/MOF.jl +++ b/test/FileFormats/MOF/MOF.jl @@ -390,7 +390,7 @@ end end @testset "Complements" begin test_model_equality( - "variables: x, y\nc1: [x, y] in Complements(1)", + "variables: x, y\nc1: [x, y] in Complements(2)", ["x", "y"], ["c1"], ) From 13ea10e5390e6566889060112f04a3685ea22e59 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 12 May 2021 08:09:36 +1200 Subject: [PATCH 4/6] Update sets.jl --- src/sets.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sets.jl b/src/sets.jl index e71a4e3819..20baf59261 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -852,7 +852,7 @@ defines the complementarity problem where `0 <= x_1 ⟂ x_3 >= 0` and """ struct Complements <: AbstractVectorSet dimension::Int - function Complements(dimension::Int) + function Complements(dimension::Integer) if !iseven(dimension) throw( ArgumentError( @@ -860,7 +860,7 @@ struct Complements <: AbstractVectorSet ), ) end - return new(dimension) + return new(convert(Int, dimension)) end end From 14876eea5fefad15845f332c4ed5b8456b622967 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 12 May 2021 08:23:29 +1200 Subject: [PATCH 5/6] Update sets.jl --- src/sets.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sets.jl b/src/sets.jl index 20baf59261..26d1a0d386 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -852,7 +852,9 @@ defines the complementarity problem where `0 <= x_1 ⟂ x_3 >= 0` and """ struct Complements <: AbstractVectorSet dimension::Int - function Complements(dimension::Integer) + # Need an explicit Int64 here, because JSON parses Int as Int64, even on + # 32-bit machines. + function Complements(dimension::Union{Int,Int64}) if !iseven(dimension) throw( ArgumentError( From 37c0fdbb5beb183582bfe77024dac650bd1cf60c Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 12 May 2021 09:17:36 +1200 Subject: [PATCH 6/6] [skip ci] modify NEWS --- NEWS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1d426b0f20..3df98290f7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,9 +14,6 @@ Breaking renames: - The `.variable_index` field in `ScalarAffineTerm` has been renamed to `.variable` - The `.variable_index_1` field in `ScalarQuadraticTerm` has been renamed to `.variable_1` - The `.variable_index_2` field in `ScalarQuadraticTerm` has been renamed to `.variable_2` -- The `dimension` argument to `Complements(dimension::Int)` should now be the - length of the corresponding function, instead of half the length. An - `ArgumentError` is thrown if `dimension` is not even. - `Constraint.RSOCBridge` has been renamed to `Constraint.RSOCtoSOCBridge` - `Constraint.SOCRBridge` has been renamed to `Constraint.SOCtoRSOCBridge` @@ -25,6 +22,9 @@ Breaking behavior changes: - `CachingOptimizer`s are now initialized as `EMPTY_OPTIMIZER` instead of `ATTACHED_OPTIMIZER`. If your code relies on the optimizer being attached, call `MOIU.attach_optimizer(model)` after creation. +- The `dimension` argument to `Complements(dimension::Int)` should now be the + length of the corresponding function, instead of half the length. An + `ArgumentError` is thrown if `dimension` is not even. v0.9.21 (April 23, 2021) ---------------------------