diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml index 6abd107..f196f10 100644 --- a/.JuliaFormatter.toml +++ b/.JuliaFormatter.toml @@ -2,3 +2,8 @@ # For more information, see: https://domluna.github.io/JuliaFormatter.jl/stable/config/ always_for_in = true +always_use_return = true +margin = 80 +remove_extra_newlines = true +separate_kwargs_with_semicolon = true +short_to_long_function_def = true diff --git a/src/Bridges/complements_vectorize_bridge.jl b/src/Bridges/complements_vectorize_bridge.jl index cdef5f6..d16dd61 100644 --- a/src/Bridges/complements_vectorize_bridge.jl +++ b/src/Bridges/complements_vectorize_bridge.jl @@ -34,7 +34,8 @@ where `T` is the coefficient type. [`MOI.Zeros`](@ref) depending on the input set type """ -struct ComplementsVectorizeBridge{T,F,S,SV} <: MOI.Bridges.Constraint.AbstractBridge +struct ComplementsVectorizeBridge{T,F,S,SV} <: + MOI.Bridges.Constraint.AbstractBridge constraint::MOI.ConstraintIndex{F,ComplementsWithSetType{SV}} set_constant::T end @@ -93,7 +94,11 @@ end function MOI.supports_constraint( ::Type{<:ComplementsVectorizeBridge}, ::Type{MOI.VectorOfVariables}, - ::Type{<:ComplementsWithSetType{<:Union{MOI.GreaterThan,MOI.LessThan,MOI.EqualTo}}}, + ::Type{ + <:ComplementsWithSetType{ + <:Union{MOI.GreaterThan,MOI.LessThan,MOI.EqualTo}, + }, + }, ) return true end @@ -109,11 +114,13 @@ function MOI.Bridges.Constraint.concrete_bridge_type( return ComplementsVectorizeBridge{T,F,S,SV} end -MOI.supports( +function MOI.supports( ::MOI.ModelLike, ::ComplementarityReformulation, ::Type{<:ComplementsVectorizeBridge}, -) = true +) + return true +end function MOI.set( model::MOI.ModelLike, @@ -127,7 +134,9 @@ end # Bridge metadata -function MOI.Bridges.added_constrained_variable_types(::Type{<:ComplementsVectorizeBridge}) +function MOI.Bridges.added_constrained_variable_types( + ::Type{<:ComplementsVectorizeBridge}, +) return Tuple{Type}[] end diff --git a/src/Bridges/flip_sign_bridge.jl b/src/Bridges/flip_sign_bridge.jl index e39ac32..33570dc 100644 --- a/src/Bridges/flip_sign_bridge.jl +++ b/src/Bridges/flip_sign_bridge.jl @@ -51,7 +51,8 @@ function _flip_set_type(::Type{MOI.LessThan{T}}) where {T} return MOI.GreaterThan{T} end -const _FlippableSets = Union{MOI.Nonnegatives,MOI.Nonpositives,MOI.GreaterThan,MOI.LessThan} +const _FlippableSets = + Union{MOI.Nonnegatives,MOI.Nonpositives,MOI.GreaterThan,MOI.LessThan} function MOI.Bridges.Constraint.bridge_constraint( ::Type{FlipSignBridge{T,F,S1,S2,G}}, @@ -87,8 +88,13 @@ function MOI.Bridges.Constraint.concrete_bridge_type( return FlipSignBridge{T,F,S1,S2,G} end -MOI.supports(::MOI.ModelLike, ::ComplementarityReformulation, ::Type{<:FlipSignBridge}) = - true +function MOI.supports( + ::MOI.ModelLike, + ::ComplementarityReformulation, + ::Type{<:FlipSignBridge}, +) + return true +end function MOI.set( model::MOI.ModelLike, @@ -126,7 +132,11 @@ function MOI.get( return [bridge.constraint] end -function MOI.get(::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::FlipSignBridge) +function MOI.get( + ::MOI.ModelLike, + ::MOI.ConstraintFunction, + bridge::FlipSignBridge, +) return bridge.original_func end diff --git a/src/Bridges/nonlinear.jl b/src/Bridges/nonlinear.jl index 879d715..4cc79df 100644 --- a/src/Bridges/nonlinear.jl +++ b/src/Bridges/nonlinear.jl @@ -47,7 +47,9 @@ function MOI.Bridges.Constraint.bridge_constraint( # Only `MathOptComplements.Optimizer` supports setting custom bridge arguments # so the default will be used when the bridge is added for instance to # `MOI.Bridges.LazyBridgeOptimizer` - reformulation::AbstractComplementarityRelaxation = ScholtesRelaxation(zero(T)), + reformulation::AbstractComplementarityRelaxation = ScholtesRelaxation( + zero(T), + ), ) where {T,S} # Delay reformulation until `final_touch` so that per-constraint # `ComplementarityReformulation` attributes can override it first. @@ -70,8 +72,13 @@ function MOI.Bridges.Constraint.concrete_bridge_type( return NonlinearBridge{T,S} end -MOI.supports(::MOI.ModelLike, ::ComplementarityReformulation, ::Type{<:NonlinearBridge}) = - true +function MOI.supports( + ::MOI.ModelLike, + ::ComplementarityReformulation, + ::Type{<:NonlinearBridge}, +) + return true +end function MOI.set( model::MOI.ModelLike, @@ -98,11 +105,17 @@ function MOI.Bridges.added_constraint_types(::Type{<:NonlinearBridge}) return Tuple{Type,Type}[] end -function MOI.get(bridge::NonlinearBridge, ::MOI.NumberOfConstraints{F,S})::Int64 where {F,S} +function MOI.get( + bridge::NonlinearBridge, + ::MOI.NumberOfConstraints{F,S}, +)::Int64 where {F,S} return count(ci -> ci isa MOI.ConstraintIndex{F,S}, bridge.constraints) end -function MOI.get(bridge::NonlinearBridge, ::MOI.ListOfConstraintIndices{F,S}) where {F,S} +function MOI.get( + bridge::NonlinearBridge, + ::MOI.ListOfConstraintIndices{F,S}, +) where {F,S} return MOI.ConstraintIndex{F,S}[ ci for ci in bridge.constraints if ci isa MOI.ConstraintIndex{F,S} ] @@ -134,19 +147,52 @@ function MOI.Bridges.final_touch(bridge::NonlinearBridge, model::MOI.ModelLike) end # Bound helpers: extract the relevant bounds based on the set type S. -_complementarity_bounds(::Type{MOI.Nonnegatives}, model, ::Type{T}, x2) where {T} = - (zero(T), T(Inf)) -_complementarity_bounds(::Type{MOI.Nonpositives}, model, ::Type{T}, x2) where {T} = - (T(-Inf), zero(T)) -_complementarity_bounds(::Type{MOI.Zeros}, model, ::Type{T}, x2) where {T} = - (zero(T), zero(T)) -function _complementarity_bounds(::Type{<:MOI.GreaterThan}, model, ::Type{T}, x2) where {T} +function _complementarity_bounds( + ::Type{MOI.Nonnegatives}, + model, + ::Type{T}, + x2, +) where {T} + return (zero(T), T(Inf)) +end +function _complementarity_bounds( + ::Type{MOI.Nonpositives}, + model, + ::Type{T}, + x2, +) where {T} + return (T(-Inf), zero(T)) +end +function _complementarity_bounds( + ::Type{MOI.Zeros}, + model, + ::Type{T}, + x2, +) where {T} + return (zero(T), zero(T)) +end +function _complementarity_bounds( + ::Type{<:MOI.GreaterThan}, + model, + ::Type{T}, + x2, +) where {T} return (MOIU.get_bounds(model, T, x2)[1], T(Inf)) end -function _complementarity_bounds(::Type{<:MOI.LessThan}, model, ::Type{T}, x2) where {T} +function _complementarity_bounds( + ::Type{<:MOI.LessThan}, + model, + ::Type{T}, + x2, +) where {T} return (T(-Inf), MOIU.get_bounds(model, T, x2)[2]) end -function _complementarity_bounds(::Type{<:MOI.Interval}, model, ::Type{T}, x2) where {T} +function _complementarity_bounds( + ::Type{<:MOI.Interval}, + model, + ::Type{T}, + x2, +) where {T} return MOIU.get_bounds(model, T, x2) end @@ -170,18 +216,38 @@ function reformulate_as_nonlinear_program!( x2 = fun.variables[cc+n_comp] lb2, ub2 = _complementarity_bounds(S, model, Float64, x2) if isinf(ub2) - idc = _relax_complementarity_lower_bound!(model, relaxation, x1, x2, lb2, ub2) + idc = _relax_complementarity_lower_bound!( + model, + relaxation, + x1, + x2, + lb2, + ub2, + ) elseif isinf(lb2) - idc = _relax_complementarity_upper_bound!(model, relaxation, x1, x2, lb2, ub2) + idc = _relax_complementarity_upper_bound!( + model, + relaxation, + x1, + x2, + lb2, + ub2, + ) else - idc = _relax_complementarity_range!(model, relaxation, x1, x2, lb2, ub2) + idc = _relax_complementarity_range!( + model, + relaxation, + x1, + x2, + lb2, + ub2, + ) end append!(ind_cc, idc) end return ind_cc end - """ ScholtesRelaxation <: AbstractComplementarityRelaxation @@ -216,7 +282,8 @@ function _relax_complementarity_lower_bound!( @assert lb1 == 0.0 # ensure we follow MOI's convention # TODO: what should we do if ub1 is finite? end - idc = MOI.add_constraint(model, x1 * (x2 - lb2), MOI.LessThan(relaxation.tau)) + idc = + MOI.add_constraint(model, x1 * (x2 - lb2), MOI.LessThan(relaxation.tau)) return [idc] end @@ -236,7 +303,8 @@ function _relax_complementarity_upper_bound!( @assert ub1 == 0.0 # ensure we follow MOI's convention # TODO: what should we do if lb1 is finite? end - idc = MOI.add_constraint(model, x1 * (x2 - ub2), MOI.LessThan(relaxation.tau)) + idc = + MOI.add_constraint(model, x1 * (x2 - ub2), MOI.LessThan(relaxation.tau)) return [idc] end @@ -249,8 +317,10 @@ function _relax_complementarity_range!( lb2, ub2, ) - idc1 = MOI.add_constraint(model, x1 * (x2 - lb2), MOI.LessThan(relaxation.tau)) - idc2 = MOI.add_constraint(model, x1 * (x2 - ub2), MOI.LessThan(relaxation.tau)) + idc1 = + MOI.add_constraint(model, x1 * (x2 - lb2), MOI.LessThan(relaxation.tau)) + idc2 = + MOI.add_constraint(model, x1 * (x2 - ub2), MOI.LessThan(relaxation.tau)) return [idc1, idc2] end @@ -361,7 +431,6 @@ function _relax_complementarity_range!( return [idc1, idc2] end - """ LiuFukushimaRelaxation <: AbstractComplementarityRelaxation @@ -389,7 +458,11 @@ function _relax_complementarity_lower_bound!( lb1, _ = MOIU.get_bounds(model, Float64, x1) @assert isinf(lb1) || iszero(lb1) - idc1 = MOI.add_constraint(model, x1 * (x2 - lb2), MOI.LessThan(relaxation.epsilon^2)) + idc1 = MOI.add_constraint( + model, + x1 * (x2 - lb2), + MOI.LessThan(relaxation.epsilon^2), + ) idc2 = MOI.add_constraint( model, (x1 + relaxation.epsilon) * (x2 - lb2 + relaxation.epsilon), @@ -398,7 +471,9 @@ function _relax_complementarity_lower_bound!( # Remove bounds _remove_bounds!(model, x1) - cidx = MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(x2.value) + cidx = MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}( + x2.value, + ) MOI.delete(model, cidx) return [idc1, idc2] @@ -416,7 +491,11 @@ function _relax_complementarity_upper_bound!( _, ub1 = MOIU.get_bounds(model, Float64, x1) @assert isinf(ub1) || iszero(ub1) - idc1 = MOI.add_constraint(model, x1 * (x2 - ub2), MOI.LessThan(relaxation.epsilon^2)) + idc1 = MOI.add_constraint( + model, + x1 * (x2 - ub2), + MOI.LessThan(relaxation.epsilon^2), + ) idc2 = MOI.add_constraint( model, (x1 - relaxation.epsilon) * (x2 - ub2 - relaxation.epsilon), @@ -425,13 +504,13 @@ function _relax_complementarity_upper_bound!( # Remove bounds _remove_bounds!(model, x1) - cidx = MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(x2.value) + cidx = + MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(x2.value) MOI.delete(model, cidx) return [idc1, idc2] end - """ KanzowSchwarzRelaxation <: AbstractComplementarityRelaxation diff --git a/src/Bridges/specify_set_type_bridge.jl b/src/Bridges/specify_set_type_bridge.jl index c07edd6..b5ba62d 100644 --- a/src/Bridges/specify_set_type_bridge.jl +++ b/src/Bridges/specify_set_type_bridge.jl @@ -77,11 +77,13 @@ function MOI.Bridges.Constraint.concrete_bridge_type( return SpecifySetTypeBridge{T} end -MOI.supports( +function MOI.supports( ::MOI.ModelLike, ::ComplementarityReformulation, ::Type{<:SpecifySetTypeBridge}, -) = true +) + return true +end function MOI.set( model::MOI.ModelLike, @@ -114,7 +116,12 @@ function MOI.Bridges.final_touch( end if bridge.reformulation !== nothing for ci in bridge.constraints - MOI.set(model, ComplementarityReformulation(), ci, bridge.reformulation) + MOI.set( + model, + ComplementarityReformulation(), + ci, + bridge.reformulation, + ) end end return @@ -130,7 +137,10 @@ function _specify_set_type_pair!(model, ::Type{T}, x1, x2, bounds) where {T} return _specify_range!(model, T, x1, x2, lb2, ub2) else # Both infinite: x1 must be zero - push!(bounds, MOI.add_constraint(model, one(T) * x1, MOI.EqualTo(zero(T)))) + push!( + bounds, + MOI.add_constraint(model, one(T) * x1, MOI.EqualTo(zero(T))), + ) return MOI.add_constraint( model, MOI.VectorOfVariables([x1, x2]), @@ -175,11 +185,15 @@ end # Bridge metadata -function MOI.Bridges.added_constrained_variable_types(::Type{<:SpecifySetTypeBridge}) +function MOI.Bridges.added_constrained_variable_types( + ::Type{<:SpecifySetTypeBridge}, +) return Tuple{Type}[] end -function MOI.Bridges.added_constraint_types(::Type{SpecifySetTypeBridge{T}}) where {T} +function MOI.Bridges.added_constraint_types( + ::Type{SpecifySetTypeBridge{T}}, +) where {T} return Tuple{Type,Type}[ (MOI.VectorOfVariables, ComplementsWithSetType{MOI.Nonnegatives}), (MOI.VectorOfVariables, ComplementsWithSetType{MOI.Nonpositives}), @@ -193,7 +207,6 @@ function MOI.Bridges.added_constraint_types(::Type{SpecifySetTypeBridge{T}}) whe ] end - function MOI.get( bridge::SpecifySetTypeBridge, ::MOI.NumberOfConstraints{F,S}, @@ -207,14 +220,24 @@ function MOI.get( ::MOI.ListOfConstraintIndices{F,S}, ) where {F,S} all_cis = [bridge.constraints; bridge.bounds] - return MOI.ConstraintIndex{F,S}[ci for ci in all_cis if ci isa MOI.ConstraintIndex{F,S}] + return MOI.ConstraintIndex{F,S}[ + ci for ci in all_cis if ci isa MOI.ConstraintIndex{F,S} + ] end -function MOI.get(::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::SpecifySetTypeBridge) +function MOI.get( + ::MOI.ModelLike, + ::MOI.ConstraintFunction, + bridge::SpecifySetTypeBridge, +) return bridge.func end -function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet, bridge::SpecifySetTypeBridge) +function MOI.get( + ::MOI.ModelLike, + ::MOI.ConstraintSet, + bridge::SpecifySetTypeBridge, +) return bridge.set end diff --git a/src/Bridges/split_interval_bridge.jl b/src/Bridges/split_interval_bridge.jl index bc68970..4084be7 100644 --- a/src/Bridges/split_interval_bridge.jl +++ b/src/Bridges/split_interval_bridge.jl @@ -39,8 +39,11 @@ must be a variable. where `G` is the scalar function type of the first component. """ -struct SplitIntervalBridge{T,G<:MOI.AbstractScalarFunction,F<:MOI.AbstractVectorFunction} <: - MOI.Bridges.Constraint.AbstractBridge +struct SplitIntervalBridge{ + T, + G<:MOI.AbstractScalarFunction, + F<:MOI.AbstractVectorFunction, +} <: MOI.Bridges.Constraint.AbstractBridge lower::MOI.ConstraintIndex{ MOI.VectorOfVariables, ComplementsWithSetType{MOI.GreaterThan{T}}, @@ -71,7 +74,9 @@ function MOI.Bridges.Constraint.bridge_constraint( y else # Extract the variable from a ScalarAffineFunction wrapping a single variable - @assert length(y.terms) == 1 && isone(y.terms[1].coefficient) && iszero(y.constant) + @assert length(y.terms) == 1 && + isone(y.terms[1].coefficient) && + iszero(y.constant) y.terms[1].variable end # Create xp >= 0 and xn <= 0 @@ -80,7 +85,8 @@ function MOI.Bridges.Constraint.bridge_constraint( # x == xp + xn eq_func = MOIU.operate(-, T, x_func, xp) eq_func = MOIU.operate!(-, T, eq_func, xn) - equality = MOIU.normalize_and_add_constraint(model, eq_func, MOI.EqualTo(zero(T))) + equality = + MOIU.normalize_and_add_constraint(model, eq_func, MOI.EqualTo(zero(T))) # [xp, y] in ComplementsWithSetType{GreaterThan{T}} lower = MOI.add_constraint( model, @@ -93,7 +99,15 @@ function MOI.Bridges.Constraint.bridge_constraint( MOI.VectorOfVariables([xn, y_var]), ComplementsWithSetType{MOI.LessThan{T}}(2), ) - return SplitIntervalBridge{T,G,typeof(func)}(lower, upper, equality, xp, xn, func, set) + return SplitIntervalBridge{T,G,typeof(func)}( + lower, + upper, + equality, + xp, + xn, + func, + set, + ) end function MOI.supports_constraint( @@ -115,11 +129,13 @@ function MOI.Bridges.Constraint.concrete_bridge_type( return SplitIntervalBridge{T,H,F} end -MOI.supports( +function MOI.supports( ::MOI.ModelLike, ::ComplementarityReformulation, ::Type{<:SplitIntervalBridge}, -) = true +) + return true +end function MOI.set( model::MOI.ModelLike, @@ -140,7 +156,9 @@ function MOI.Bridges.added_constrained_variable_types( return Tuple{Type}[(MOI.GreaterThan{T},), (MOI.LessThan{T},)] end -function MOI.Bridges.added_constraint_types(::Type{<:SplitIntervalBridge{T,G}}) where {T,G} +function MOI.Bridges.added_constraint_types( + ::Type{<:SplitIntervalBridge{T,G}}, +) where {T,G} return Tuple{Type,Type}[ (MOI.VectorOfVariables, ComplementsWithSetType{MOI.GreaterThan{T}}), (MOI.VectorOfVariables, ComplementsWithSetType{MOI.LessThan{T}}), @@ -170,7 +188,11 @@ function MOI.get( bridge::SplitIntervalBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.GreaterThan{T}}, ) where {T} - return [MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{T}}(bridge.xp.value)] + return [ + MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{T}}( + bridge.xp.value, + ), + ] end function MOI.get( @@ -184,7 +206,9 @@ function MOI.get( bridge::SplitIntervalBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.LessThan{T}}, ) where {T} - return [MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{T}}(bridge.xn.value)] + return [ + MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{T}}(bridge.xn.value), + ] end function MOI.get( @@ -241,11 +265,19 @@ function MOI.get( return [bridge.equality] end -function MOI.get(::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::SplitIntervalBridge) +function MOI.get( + ::MOI.ModelLike, + ::MOI.ConstraintFunction, + bridge::SplitIntervalBridge, +) return bridge.func end -function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet, bridge::SplitIntervalBridge) +function MOI.get( + ::MOI.ModelLike, + ::MOI.ConstraintSet, + bridge::SplitIntervalBridge, +) return bridge.set end diff --git a/src/Bridges/to_sos1_bridge.jl b/src/Bridges/to_sos1_bridge.jl index 57055cb..2b0c3f3 100644 --- a/src/Bridges/to_sos1_bridge.jl +++ b/src/Bridges/to_sos1_bridge.jl @@ -83,7 +83,11 @@ function MOI.get( return [bridge.sos1] end -function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::ToSOS1Bridge) +function MOI.get( + model::MOI.ModelLike, + ::MOI.ConstraintFunction, + bridge::ToSOS1Bridge, +) return MOI.get(model, MOI.ConstraintFunction(), bridge.sos1) end diff --git a/src/Bridges/vertical.jl b/src/Bridges/vertical.jl index 40a5ba6..d2ffa4a 100644 --- a/src/Bridges/vertical.jl +++ b/src/Bridges/vertical.jl @@ -31,7 +31,8 @@ to an equality constraint instead. * [`MOI.ScalarAffineFunction{T}`](@ref) in [`MOI.EqualTo{T}`](@ref) """ -struct VerticalBridge{T,S<:MOI.AbstractVectorSet} <: MOI.Bridges.Constraint.AbstractBridge +struct VerticalBridge{T,S<:MOI.AbstractVectorSet} <: + MOI.Bridges.Constraint.AbstractBridge constraint::MOI.ConstraintIndex{MOI.VectorOfVariables,S} equalities::Vector{MOI.ConstraintIndex} slacks::Vector{MOI.VariableIndex} @@ -95,7 +96,9 @@ function MOI.Bridges.added_constrained_variable_types(::Type{<:VerticalBridge}) return Tuple{Type}[(MOI.Reals,)] end -function MOI.Bridges.added_constraint_types(::Type{VerticalBridge{T,S}}) where {T,S} +function MOI.Bridges.added_constraint_types( + ::Type{VerticalBridge{T,S}}, +) where {T,S} return Tuple{Type,Type}[ (MOI.VectorOfVariables, S), (MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}), @@ -129,7 +132,9 @@ function MOI.get( ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}, )::Int64 where {T} return count( - ci -> ci isa MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}, + ci -> + ci isa + MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}, bridge.equalities, ) end @@ -155,8 +160,13 @@ function MOI.delete(model::MOI.ModelLike, bridge::VerticalBridge) return end -MOI.supports(::MOI.ModelLike, ::ComplementarityReformulation, ::Type{<:VerticalBridge}) = - true +function MOI.supports( + ::MOI.ModelLike, + ::ComplementarityReformulation, + ::Type{<:VerticalBridge}, +) + return true +end function MOI.set( model::MOI.ModelLike, @@ -186,15 +196,19 @@ function _is_single_variable(func::MOI.ScalarQuadraticFunction) ) end function _is_single_variable(func::MOI.ScalarNonlinearFunction) - return func.head == :+ && length(func.args) == 1 && isa(func.args[1], MOI.VariableIndex) + return func.head == :+ && + length(func.args) == 1 && + isa(func.args[1], MOI.VariableIndex) end _get_variable(func::MOI.ScalarAffineFunction) = func.terms[1].variable _get_variable(func::MOI.ScalarQuadraticFunction) = func.affine_terms[1].variable _get_variable(func::MOI.ScalarNonlinearFunction) = func.args[1] - # TODO: add support for ScalarNonlinearTerm -function _parse_complementarity_constraint(fun::MOI.AbstractVectorFunction, n_comp) +function _parse_complementarity_constraint( + fun::MOI.AbstractVectorFunction, + n_comp, +) exprs = MOIU.scalarize(fun) @assert length(exprs) == 2*n_comp @@ -238,7 +252,12 @@ the generated equality constraints. Once reformulated, the complementarity constraints involve only single variables. """ -function reformulate_to_vertical!(model::MOI.ModelLike, ::Type{T}, fun, set) where {T} +function reformulate_to_vertical!( + model::MOI.ModelLike, + ::Type{T}, + fun, + set, +) where {T} equalities = MOI.ConstraintIndex[] slacks = MOI.VariableIndex[] ind_cc1, ind_cc2 = MOI.VariableIndex[], MOI.VariableIndex[] @@ -254,7 +273,11 @@ function reformulate_to_vertical!(model::MOI.ModelLike, ::Type{T}, fun, set) whe # If x2 is unbounded, the LHS is directly converted to an equality constraint. push!( equalities, - MOIU.normalize_and_add_constraint(model, lhs, MOI.EqualTo{T}(zero(T))), + MOIU.normalize_and_add_constraint( + model, + lhs, + MOI.EqualTo{T}(zero(T)), + ), ) continue end @@ -272,7 +295,11 @@ function reformulate_to_vertical!(model::MOI.ModelLike, ::Type{T}, fun, set) whe new_lhs = MOIU.operate!(-, T, lhs, x1) push!( equalities, - MOIU.normalize_and_add_constraint(model, new_lhs, MOI.EqualTo{T}(zero(T))), + MOIU.normalize_and_add_constraint( + model, + new_lhs, + MOI.EqualTo{T}(zero(T)), + ), ) push!(ind_cc1, x1) push!(ind_cc2, x2) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 58471fe..e00f918 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -3,7 +3,8 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. -mutable struct Optimizer{T,O<:MOI.ModelLike} <: MOI.Bridges.AbstractBridgeOptimizer +mutable struct Optimizer{T,O<:MOI.ModelLike} <: + MOI.Bridges.AbstractBridgeOptimizer model::O # This need to be called `model` by convention of `AbstractBridgeOptimizer` reformulation::AbstractComplementarityRelaxation constraint_map::MOI.Bridges.Constraint.Map @@ -44,62 +45,86 @@ MOI.Bridges.is_bridged(::Optimizer, ::Type{<:MOI.AbstractSet}) = false # Complements and ComplementsWithSetType are bridged MOI.Bridges.is_bridged(::Optimizer, ::Type{MOI.Complements}) = true -MOI.Bridges.supports_bridging_constrained_variable(::Optimizer, ::Type{MOI.Complements}) = - true -MOI.Bridges.bridge_type(::Optimizer{T}, ::Type{MOI.Complements}) where {T} = - Bridges.SpecifySetTypeBridge{T} +function MOI.Bridges.supports_bridging_constrained_variable( + ::Optimizer, + ::Type{MOI.Complements}, +) + return true +end +function MOI.Bridges.bridge_type( + ::Optimizer{T}, + ::Type{MOI.Complements}, +) where {T} + return Bridges.SpecifySetTypeBridge{T} +end MOI.Bridges.is_bridged(::Optimizer, ::Type{<:ComplementsWithSetType}) = true -MOI.Bridges.supports_bridging_constrained_variable( +function MOI.Bridges.supports_bridging_constrained_variable( ::Optimizer, ::Type{<:ComplementsWithSetType}, -) = true +) + return true +end # No objective bridge MOI.Bridges.is_bridged(::Optimizer, ::Type{<:MOI.AbstractFunction}) = false # We only bridge Complements and ComplementsWithSetType constraints -MOI.Bridges.is_bridged( +function MOI.Bridges.is_bridged( ::Optimizer, ::Type{<:MOI.AbstractFunction}, ::Type{<:MOI.AbstractSet}, -) = false +) + return false +end # Expression-based Complements → Bridges.VerticalBridge -MOI.Bridges.is_bridged( +function MOI.Bridges.is_bridged( ::Optimizer, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.Complements}, -) = true -MOI.Bridges.supports_bridging_constraint( +) + return true +end +function MOI.Bridges.supports_bridging_constraint( ::Optimizer, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.Complements}, -) = true -MOI.Bridges.bridge_type( +) + return true +end +function MOI.Bridges.bridge_type( ::Optimizer{T}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.Complements}, -) where {T} = Bridges.VerticalBridge{T,MOI.Complements} +) where {T} + return Bridges.VerticalBridge{T,MOI.Complements} +end # VectorOfVariables-in-Complements → Bridges.SpecifySetTypeBridge{T} -MOI.Bridges.bridge_type( +function MOI.Bridges.bridge_type( ::Optimizer{T}, ::Type{<:MOI.VectorOfVariables}, ::Type{MOI.Complements}, -) where {T} = Bridges.SpecifySetTypeBridge{T} +) where {T} + return Bridges.SpecifySetTypeBridge{T} +end # ComplementsWithSetType{S} → bridge selection depends on inner solver -MOI.Bridges.is_bridged( +function MOI.Bridges.is_bridged( ::Optimizer, ::Type{<:MOI.AbstractVectorFunction}, ::Type{<:ComplementsWithSetType}, -) = true -MOI.Bridges.supports_bridging_constraint( +) + return true +end +function MOI.Bridges.supports_bridging_constraint( ::Optimizer, ::Type{<:MOI.AbstractVectorFunction}, ::Type{<:ComplementsWithSetType}, -) = true +) + return true +end # --- NLP path: inner solver supports ScalarNonlinearFunction --- # NonlinearBridge handles all set types directly via relaxation methods. @@ -138,36 +163,49 @@ end # The goal is to reach VOV-in-ComplementsWithSetType{Nonnegatives} → ToSOS1Bridge. # Interval → SplitInterval (split into GreaterThan + LessThan) -_sos1_bridge_type( +function _sos1_bridge_type( ::Type{T}, ::Type{MOI.VectorOfVariables}, ::Type{<:MOI.Interval}, -) where {T} = Bridges.SplitIntervalBridge{T} +) where {T} + return Bridges.SplitIntervalBridge{T} +end # LessThan/Nonpositives → FlipSign (negate activity to get GreaterThan/Nonnegatives) -_sos1_bridge_type( +function _sos1_bridge_type( ::Type{T}, ::Type{MOI.VectorOfVariables}, ::Type{<:Union{MOI.LessThan,MOI.Nonpositives}}, -) where {T} = Bridges.FlipSignBridge{T} +) where {T} + return Bridges.FlipSignBridge{T} +end # VOV-in-GreaterThan/EqualTo → Vectorize (shift to Nonneg/Zeros) -_sos1_bridge_type( +function _sos1_bridge_type( ::Type{T}, ::Type{MOI.VectorOfVariables}, ::Type{<:Union{MOI.GreaterThan,MOI.EqualTo}}, -) where {T} = Bridges.ComplementsVectorizeBridge{T} +) where {T} + return Bridges.ComplementsVectorizeBridge{T} +end # VOV-in-Nonnegatives → ToSOS1Bridge (final target) -_sos1_bridge_type( +function _sos1_bridge_type( ::Type{T}, ::Type{MOI.VectorOfVariables}, ::Type{MOI.Nonnegatives}, -) where {T} = Bridges.ToSOS1Bridge{T} +) where {T} + return Bridges.ToSOS1Bridge{T} +end # VOV-in-Zeros → ToSOS1Bridge (trivial complementarity) -_sos1_bridge_type(::Type{T}, ::Type{MOI.VectorOfVariables}, ::Type{MOI.Zeros}) where {T} = - Bridges.ToSOS1Bridge{T} +function _sos1_bridge_type( + ::Type{T}, + ::Type{MOI.VectorOfVariables}, + ::Type{MOI.Zeros}, +) where {T} + return Bridges.ToSOS1Bridge{T} +end # Any non-VOV function → VerticalBridge (create slacks, then re-enter as VOV) function _sos1_bridge_type( @@ -196,11 +234,19 @@ function MOI.set( return end -MOI.Utilities.map_indices(::Function, relax::AbstractComplementarityRelaxation) = relax +function MOI.Utilities.map_indices( + ::Function, + relax::AbstractComplementarityRelaxation, +) + return relax +end _additional_arguments(::Optimizer, ::Type) = tuple() -function _additional_arguments(model::Optimizer, ::Type{<:Bridges.NonlinearBridge}) +function _additional_arguments( + model::Optimizer, + ::Type{<:Bridges.NonlinearBridge}, +) # Create a 1-element tuple since it is splatted in `add_bridged_constraint` return (model.reformulation,) end diff --git a/src/utils.jl b/src/utils.jl index 2817c22..62a419e 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -7,7 +7,9 @@ function _remove_bounds!(model::MOI.ModelLike, x::MOI.VariableIndex) for cidx in [ MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{Float64}}(x.value), MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(x.value), - MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(x.value), + MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}( + x.value, + ), ] if MOI.is_valid(model, cidx) MOI.delete(model, cidx) diff --git a/test/Bridges/complements_vectorize_bridge.jl b/test/Bridges/complements_vectorize_bridge.jl index f535916..9f0ae12 100644 --- a/test/Bridges/complements_vectorize_bridge.jl +++ b/test/Bridges/complements_vectorize_bridge.jl @@ -13,16 +13,26 @@ using MathOptComplements MathOptComplements.Bridges.ComplementsVectorizeBridge{Float64}, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(3.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.GreaterThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.GreaterThan{Float64}, + }( + 2, + ), ) end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(3.0), + ) f = MOI.VectorAffineFunction{Float64}( [ MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x)), @@ -33,7 +43,9 @@ using MathOptComplements MOI.add_constraint( model, f, - MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}( + 2, + ), ) end; cannot_unbridge = true, @@ -45,16 +57,22 @@ using MathOptComplements MathOptComplements.Bridges.ComplementsVectorizeBridge{Float64}, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.LessThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.LessThan{Float64}, + }( + 2, + ), ) end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) f = MOI.VectorAffineFunction{Float64}( [ MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x)), @@ -65,7 +83,9 @@ using MathOptComplements MOI.add_constraint( model, f, - MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}( + 2, + ), ) end; cannot_unbridge = true, @@ -77,16 +97,22 @@ using MathOptComplements MathOptComplements.Bridges.ComplementsVectorizeBridge{Float64}, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.EqualTo(2.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.EqualTo(2.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.EqualTo{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.EqualTo{Float64}, + }( + 2, + ), ) end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.EqualTo(2.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.EqualTo(2.0)) f = MOIU.operate(vcat, Float64, 1.0 * x, 1.0 * y - 2.0) MOI.add_constraint( model, diff --git a/test/Bridges/flip_sign_bridge.jl b/test/Bridges/flip_sign_bridge.jl index 90d32e2..2d316ee 100644 --- a/test/Bridges/flip_sign_bridge.jl +++ b/test/Bridges/flip_sign_bridge.jl @@ -13,21 +13,31 @@ using MathOptComplements MathOptComplements.Bridges.FlipSignBridge, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}( + 2, + ), ) end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) f = MOIU.operate(vcat, Float64, -1.0 * x, 1.0 * y) MOI.add_constraint( model, f, - MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}( + 2, + ), ) end; cannot_unbridge = true, @@ -39,21 +49,27 @@ using MathOptComplements MathOptComplements.Bridges.FlipSignBridge, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(0.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}( + 2, + ), ) end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(0.0)) f = MOIU.operate(vcat, Float64, -1.0 * x, 1.0 * y) MOI.add_constraint( model, f, - MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}( + 2, + ), ) end; cannot_unbridge = true, @@ -65,21 +81,35 @@ using MathOptComplements MathOptComplements.Bridges.FlipSignBridge, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(3.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.GreaterThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.GreaterThan{Float64}, + }( + 2, + ), ) end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(3.0), + ) f = MOIU.operate(vcat, Float64, -1.0 * x, 1.0 * y) MOI.add_constraint( model, f, - MathOptComplements.ComplementsWithSetType{MOI.LessThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.LessThan{Float64}, + }( + 2, + ), ) end; cannot_unbridge = true, @@ -91,21 +121,31 @@ using MathOptComplements MathOptComplements.Bridges.FlipSignBridge, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.LessThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.LessThan{Float64}, + }( + 2, + ), ) end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) f = MOIU.operate(vcat, Float64, -1.0 * x, 1.0 * y) MOI.add_constraint( model, f, - MathOptComplements.ComplementsWithSetType{MOI.GreaterThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.GreaterThan{Float64}, + }( + 2, + ), ) end; cannot_unbridge = true, diff --git a/test/Bridges/nonlinear.jl b/test/Bridges/nonlinear.jl index 520cb7f..da2a8f4 100644 --- a/test/Bridges/nonlinear.jl +++ b/test/Bridges/nonlinear.jl @@ -13,16 +13,24 @@ using MathOptComplements MathOptComplements.Bridges.NonlinearBridge, model -> begin x1, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - x2, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + x2, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x1, x2]), - MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}( + 2, + ), ) end, model -> begin x1, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - x2, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + x2, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) # x1 * (x2 - 0) <= 0 MOI.add_constraint(model, x1 * (x2 - 0.0), MOI.LessThan(0.0)) end; @@ -35,16 +43,20 @@ using MathOptComplements MathOptComplements.Bridges.NonlinearBridge, model -> begin x1, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - x2, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) + x2, _ = + MOI.add_constrained_variable(model, MOI.LessThan(0.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x1, x2]), - MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}( + 2, + ), ) end, model -> begin x1, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - x2, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) + x2, _ = + MOI.add_constrained_variable(model, MOI.LessThan(0.0)) # x1 * (x2 - 0) <= 0 MOI.add_constraint(model, x1 * (x2 - 0.0), MOI.LessThan(0.0)) end; @@ -57,16 +69,26 @@ using MathOptComplements MathOptComplements.Bridges.NonlinearBridge, model -> begin x1, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - x2, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0)) + x2, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(3.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x1, x2]), - MathOptComplements.ComplementsWithSetType{MOI.GreaterThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.GreaterThan{Float64}, + }( + 2, + ), ) end, model -> begin x1, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - x2, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0)) + x2, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(3.0), + ) # x1 * (x2 - 3) <= 0 MOI.add_constraint(model, x1 * (x2 - 3.0), MOI.LessThan(0.0)) end; @@ -123,16 +145,22 @@ using MathOptComplements MathOptComplements.Bridges.NonlinearBridge, model -> begin x1, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - x2, _ = MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) + x2, _ = + MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x1, x2]), - MathOptComplements.ComplementsWithSetType{MOI.LessThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.LessThan{Float64}, + }( + 2, + ), ) end, model -> begin x1, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - x2, _ = MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) + x2, _ = + MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) # x1 * (x2 - (-2)) <= 0 MOI.add_constraint(model, x1 * (x2 + 2.0), MOI.LessThan(0.0)) end; @@ -155,15 +183,26 @@ using MathOptComplements MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), ) relax = MathOptComplements.FischerBurmeisterRelaxation(1e-8) - MOI.set(model, MathOptComplements.ComplementarityReformulation(), ci, relax) + MOI.set( + model, + MathOptComplements.ComplementarityReformulation(), + ci, + relax, + ) MOI.Bridges.final_touch(model) @test MOI.get( inner, - MOI.NumberOfConstraints{MOI.VariableIndex,MOI.GreaterThan{Float64}}(), + MOI.NumberOfConstraints{ + MOI.VariableIndex, + MOI.GreaterThan{Float64}, + }(), ) == 2 @test MOI.get( inner, - MOI.NumberOfConstraints{MOI.ScalarNonlinearFunction,MOI.LessThan{Float64}}(), + MOI.NumberOfConstraints{ + MOI.ScalarNonlinearFunction, + MOI.LessThan{Float64}, + }(), ) == 1 end @@ -182,7 +221,12 @@ using MathOptComplements MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}(2), ) relax = MathOptComplements.FischerBurmeisterRelaxation(1e-8) - MOI.set(model, MathOptComplements.ComplementarityReformulation(), ci, relax) + MOI.set( + model, + MathOptComplements.ComplementarityReformulation(), + ci, + relax, + ) MOI.Bridges.final_touch(model) @test MOI.get( inner, @@ -190,7 +234,10 @@ using MathOptComplements ) == 2 @test MOI.get( inner, - MOI.NumberOfConstraints{MOI.ScalarNonlinearFunction,MOI.GreaterThan{Float64}}(), + MOI.NumberOfConstraints{ + MOI.ScalarNonlinearFunction, + MOI.GreaterThan{Float64}, + }(), ) == 1 end @@ -209,11 +256,19 @@ using MathOptComplements MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), ) relax = MathOptComplements.KanzowSchwarzRelaxation(1e-8) - MOI.set(model, MathOptComplements.ComplementarityReformulation(), ci, relax) + MOI.set( + model, + MathOptComplements.ComplementarityReformulation(), + ci, + relax, + ) MOI.Bridges.final_touch(model) @test MOI.get( inner, - MOI.NumberOfConstraints{MOI.VariableIndex,MOI.GreaterThan{Float64}}(), + MOI.NumberOfConstraints{ + MOI.VariableIndex, + MOI.GreaterThan{Float64}, + }(), ) == 2 end @@ -232,7 +287,12 @@ using MathOptComplements MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}(2), ) relax = MathOptComplements.KanzowSchwarzRelaxation(1e-8) - MOI.set(model, MathOptComplements.ComplementarityReformulation(), ci, relax) + MOI.set( + model, + MathOptComplements.ComplementarityReformulation(), + ci, + relax, + ) MOI.Bridges.final_touch(model) @test MOI.get( inner, diff --git a/test/Bridges/specify_set_type_bridge.jl b/test/Bridges/specify_set_type_bridge.jl index 1ff4fdb..ae35ec6 100644 --- a/test/Bridges/specify_set_type_bridge.jl +++ b/test/Bridges/specify_set_type_bridge.jl @@ -13,7 +13,10 @@ using MathOptComplements MathOptComplements.Bridges.SpecifySetTypeBridge, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), @@ -22,11 +25,16 @@ using MathOptComplements end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}( + 2, + ), ) end; cannot_unbridge = true, @@ -38,7 +46,10 @@ using MathOptComplements MathOptComplements.Bridges.SpecifySetTypeBridge, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(3.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), @@ -47,11 +58,18 @@ using MathOptComplements end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(3.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.GreaterThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.GreaterThan{Float64}, + }( + 2, + ), ) end; cannot_unbridge = true, @@ -63,7 +81,8 @@ using MathOptComplements MathOptComplements.Bridges.SpecifySetTypeBridge, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(0.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), @@ -72,11 +91,14 @@ using MathOptComplements end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(0.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonpositives}( + 2, + ), ) end; cannot_unbridge = true, @@ -88,7 +110,8 @@ using MathOptComplements MathOptComplements.Bridges.SpecifySetTypeBridge, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), @@ -97,11 +120,16 @@ using MathOptComplements end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.LessThan(-2.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.LessThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.LessThan{Float64}, + }( + 2, + ), ) end; cannot_unbridge = true, @@ -113,7 +141,10 @@ using MathOptComplements MathOptComplements.Bridges.SpecifySetTypeBridge, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.Interval(0.0, 1.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.Interval(0.0, 1.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), @@ -122,11 +153,18 @@ using MathOptComplements end, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.Interval(0.0, 1.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.Interval(0.0, 1.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.Interval{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.Interval{Float64}, + }( + 2, + ), ) end; cannot_unbridge = true, diff --git a/test/Bridges/split_interval_bridge.jl b/test/Bridges/split_interval_bridge.jl index 8dad3f5..5dfb092 100644 --- a/test/Bridges/split_interval_bridge.jl +++ b/test/Bridges/split_interval_bridge.jl @@ -13,29 +13,55 @@ using MathOptComplements MathOptComplements.Bridges.SplitIntervalBridge, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.Interval(0.0, 1.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.Interval(0.0, 1.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), - MathOptComplements.ComplementsWithSetType{MOI.Interval{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.Interval{Float64}, + }( + 2, + ), ) end, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.Interval(0.0, 1.0)) - xp, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - xn, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.Interval(0.0, 1.0), + ) + xp, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) + xn, _ = + MOI.add_constrained_variable(model, MOI.LessThan(0.0)) # x == xp + xn → x - xp - xn == 0 - MOI.add_constraint(model, 1.0 * x - 1.0 * xp - 1.0 * xn, MOI.EqualTo(0.0)) + MOI.add_constraint( + model, + 1.0 * x - 1.0 * xp - 1.0 * xn, + MOI.EqualTo(0.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([xp, y]), - MathOptComplements.ComplementsWithSetType{MOI.GreaterThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.GreaterThan{Float64}, + }( + 2, + ), ) MOI.add_constraint( model, MOI.VectorOfVariables([xn, y]), - MathOptComplements.ComplementsWithSetType{MOI.LessThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.LessThan{Float64}, + }( + 2, + ), ) end; cannot_unbridge = true, @@ -47,7 +73,10 @@ using MathOptComplements MathOptComplements.Bridges.SplitIntervalBridge, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.Interval(0.0, 1.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.Interval(0.0, 1.0), + ) f = MOI.VectorAffineFunction{Float64}( [ MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(2.0, x)), @@ -58,29 +87,51 @@ using MathOptComplements MOI.add_constraint( model, f, - MathOptComplements.ComplementsWithSetType{MOI.Interval{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.Interval{Float64}, + }( + 2, + ), ) end, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.Interval(0.0, 1.0)) - xp, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - xn, _ = MOI.add_constrained_variable(model, MOI.LessThan(0.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.Interval(0.0, 1.0), + ) + xp, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) + xn, _ = + MOI.add_constrained_variable(model, MOI.LessThan(0.0)) # 2x + 1 == xp + xn → 2x - xp - xn == -1 - MOI.add_constraint(model, 2.0 * x - 1.0 * xp - 1.0 * xn, MOI.EqualTo(-1.0)) + MOI.add_constraint( + model, + 2.0 * x - 1.0 * xp - 1.0 * xn, + MOI.EqualTo(-1.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([xp, y]), - MathOptComplements.ComplementsWithSetType{MOI.GreaterThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.GreaterThan{Float64}, + }( + 2, + ), ) MOI.add_constraint( model, MOI.VectorOfVariables([xn, y]), - MathOptComplements.ComplementsWithSetType{MOI.LessThan{Float64}}(2), + MathOptComplements.ComplementsWithSetType{ + MOI.LessThan{Float64}, + }( + 2, + ), ) end; cannot_unbridge = true, ) end - end diff --git a/test/Bridges/to_sos1_bridge.jl b/test/Bridges/to_sos1_bridge.jl index 29f7747..ca810af 100644 --- a/test/Bridges/to_sos1_bridge.jl +++ b/test/Bridges/to_sos1_bridge.jl @@ -12,7 +12,8 @@ using MathOptComplements MathOptComplements.Bridges.ToSOS1Bridge, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + y, _ = + MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) MOI.add_constraint( model, MOI.VectorOfVariables([x, y]), @@ -21,8 +22,13 @@ using MathOptComplements end, model -> begin x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) - MOI.add_constraint(model, MOI.VectorOfVariables([x, y]), MOI.SOS1([1.0, 2.0])) + y, _ = + MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + MOI.add_constraint( + model, + MOI.VectorOfVariables([x, y]), + MOI.SOS1([1.0, 2.0]), + ) end; cannot_unbridge = true, ) diff --git a/test/Bridges/vertical.jl b/test/Bridges/vertical.jl index d2a3b87..7a7525b 100644 --- a/test/Bridges/vertical.jl +++ b/test/Bridges/vertical.jl @@ -15,7 +15,10 @@ using MathOptComplements x = MOI.add_variable(model) z = MOI.add_variable(model) y = MOI.add_variable(model) - w, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + w, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) f = MOI.VectorAffineFunction{Float64}( [ MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(2.0, x)), @@ -31,7 +34,10 @@ using MathOptComplements x = MOI.add_variable(model) z = MOI.add_variable(model) y = MOI.add_variable(model) - w, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + w, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) # 2x + 3 = 0 → 2x in EqualTo(-3.0) (y is unbounded) MOI.add_constraint(model, 2.0 * x, MOI.EqualTo(-3.0)) MOI.add_constraint( @@ -49,7 +55,10 @@ using MathOptComplements MathOptComplements.Bridges.VerticalBridge, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) f = MOI.VectorAffineFunction{Float64}( [ MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(2.0, x)), @@ -60,19 +69,30 @@ using MathOptComplements MOI.add_constraint( model, f, - MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}( + 2, + ), ) end, model -> begin x = MOI.add_variable(model) - y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(0.0)) + y, _ = MOI.add_constrained_variable( + model, + MOI.GreaterThan(0.0), + ) x1 = MOI.add_variable(model) # 2x + 3 = x1 → 2x - x1 in EqualTo(-3.0) - MOI.add_constraint(model, 2.0 * x - 1.0 * x1, MOI.EqualTo(-3.0)) + MOI.add_constraint( + model, + 2.0 * x - 1.0 * x1, + MOI.EqualTo(-3.0), + ) MOI.add_constraint( model, MOI.VectorOfVariables([x1, y]), - MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}(2), + MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives}( + 2, + ), ) end; cannot_unbridge = true, diff --git a/test/instances.jl b/test/instances.jl index ae7931b..5fac208 100644 --- a/test/instances.jl +++ b/test/instances.jl @@ -104,7 +104,8 @@ function design_centering_model() for k in 1:3 @constraint( model, - [-(y[1, k] - x[1])^2 - (y[2, k] - x[2])^2 + x[3]^2, l[k]] ∈ MOI.Complements(2) + [-(y[1, k] - x[1])^2 - (y[2, k] - x[2])^2 + x[3]^2, l[k]] ∈ + MOI.Complements(2) ) end return model @@ -145,7 +146,10 @@ function desilva_model() @variable(model, 0.0 <= l[1:2]) @objective(model, Min, x[1]^2 - 2*x[1] + x[2]^2 - 2*x[2] + y[1]^2 + y[2]^2) for i in 1:2 - @constraint(model, 2.0*y[i] - 2.0*x[i] + 2.0 * (y[i] - 1.0) * l[i] == 0.0) + @constraint( + model, + 2.0*y[i] - 2.0*x[i] + 2.0 * (y[i] - 1.0) * l[i] == 0.0 + ) @constraint(model, [0.25 - (y[i] - 1.0)^2, l[i]] ∈ MOI.Complements(2)) end return model @@ -216,7 +220,8 @@ function hakonsen_model_broken() @constraint( model, consum2[i=1:2], - [(x[i] * (3.0 * p[i] * (1+t[i])) - 100.0 * pL), p[i]] ∈ MOI.Complements(2) + [(x[i] * (3.0 * p[i] * (1+t[i])) - 100.0 * pL), p[i]] ∈ + MOI.Complements(2) ) @constraint(model, L*pL == sum(x[i] * p[i] for i in 1:2) + l*pL + G) @constraint(model, revenue, sum(p[i] * t[i] * x[i] for i in 1:2) >= G) @@ -359,7 +364,9 @@ function water_net_model() :N => 3500, ) - dist = Dict((i, j) => sqrt((x[i] - x[j])^2 + (y[i] - y[j])^2) for (i, j) in arcs) + dist = Dict( + (i, j) => sqrt((x[i] - x[j])^2 + (y[i] - y[j])^2) for (i, j) in arcs + ) dpow = 5.33 # ... power on diameter in pressure loss equation dmin = 0.15 # ... minimum diameter of pipe @@ -386,14 +393,22 @@ function water_net_model() @variable(model, 0 <= qp[arcs]) @variable(model, 0 <= qn[arcs]) @variable(model, dmin <= d[arcs] <= dmax, start=davg) - @variable(model, h[i in nodes], start=hl[i] + 5.0, lower_bound=hl[i], upper_bound=100) + @variable( + model, + h[i in nodes], + start=hl[i] + 5.0, + lower_bound=hl[i], + upper_bound=100 + ) @variable(model, 0 <= s[i in reservoirs] <= supply[i], start=rr*supply[i]) @objective( model, Min, - sum(s[i] * pcost[i] * (h[i] - height[i]) + s[i] * wcost[i] for i in reservoirs) / - r + + sum( + s[i] * pcost[i] * (h[i] - height[i]) + s[i] * wcost[i] for + i in reservoirs + ) / r + dprc * sum(dist[(i, j)] * d[(i, j)]^cpow for (i, j) in arcs) + sum(qp[(i, j)] + qn[(i, j)] for (i, j) in arcs), ) @@ -416,7 +431,11 @@ function water_net_model() hloss * dist[(i, j)] * (qp[(i, j)]^2 - qn[(i, j)]^2) / (d[(i, j)]^dpow), ) # ... complementarity - @constraint(model, [(i, j) in arcs], [qp[(i, j)], qn[(i, j)]] ∈ MOI.Complements(2)) + @constraint( + model, + [(i, j) in arcs], + [qp[(i, j)], qn[(i, j)]] ∈ MOI.Complements(2) + ) return model end diff --git a/test/runtests.jl b/test/runtests.jl index 362e74a..3453cc2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -165,14 +165,19 @@ function test_nonlinear_mispecified_3() return model end -expected_models = - Dict(Instances.fletcher_leyffer_ex1_model => fletcher_leyffer_ex1_nonlinear_model) +expected_models = Dict( + Instances.fletcher_leyffer_ex1_model => + fletcher_leyffer_ex1_nonlinear_model, +) # The tests that do not set `DefaultComplementarityReformulation` should work # both with `MathOptComplements.Optimizer` and with a `MOI.Bridges.LazyBridgeOptimizer` # in which all `MathOptComplements` bridges have been registered via `add_all_bridges`. const OPTIMIZER_FACTORIES = [ - ("MathOptComplements.Optimizer", inner -> MathOptComplements.Optimizer(inner)), + ( + "MathOptComplements.Optimizer", + inner -> MathOptComplements.Optimizer(inner), + ), ( "LazyBridgeOptimizer + add_all_bridges", inner -> begin @@ -209,7 +214,10 @@ function test_nonlinear_expr(original_model, reformulated_model) set_optimizer(model, () -> MathOptComplements.Optimizer(inner)) MOI.Utilities.attach_optimizer(model) expected = reformulated_model() - MOI.Bridges._test_structural_identical(unsafe_backend(model).model, backend(expected)) + return MOI.Bridges._test_structural_identical( + unsafe_backend(model).model, + backend(expected), + ) end @testset "Test vertical formulation ($(opt_name))" for (opt_name, make_opt) in @@ -227,7 +235,7 @@ end instances = filter(names(Instances; all = true)) do name # The function types start with `#` s = String(name) - endswith(s, "_model") && !startswith(s, "#") + return endswith(s, "_model") && !startswith(s, "#") end @testset "$(opt_name): $name" for (opt_name, make_opt) in OPTIMIZER_FACTORIES, @@ -241,7 +249,10 @@ end reformulated_model, ) in [ (nonlinear_test_model, nonlinear_test_reformulated_model), - (Instances.fletcher_leyffer_ex1_model, fletcher_leyffer_ex1_nonlinear_model), + ( + Instances.fletcher_leyffer_ex1_model, + fletcher_leyffer_ex1_nonlinear_model, + ), ] test_nonlinear_expr(original_model, reformulated_model) end @@ -254,22 +265,44 @@ end ] @testset "Test reformulation" begin model = test_nonlinear_reformulation() - set_optimizer(model, () -> MathOptComplements.Optimizer(Ipopt.Optimizer())) - MOI.set(model, MathOptComplements.DefaultComplementarityReformulation(), relax) + set_optimizer( + model, + () -> MathOptComplements.Optimizer(Ipopt.Optimizer()), + ) + MOI.set( + model, + MathOptComplements.DefaultComplementarityReformulation(), + relax, + ) MOI.Utilities.attach_optimizer(model) - for test_func in (test_nonlinear_mispecified_1, test_nonlinear_mispecified_2) + for test_func in + (test_nonlinear_mispecified_1, test_nonlinear_mispecified_2) model = test_func() - set_optimizer(model, () -> MathOptComplements.Optimizer(Ipopt.Optimizer())) - MOI.set(model, MathOptComplements.DefaultComplementarityReformulation(), relax) + set_optimizer( + model, + () -> MathOptComplements.Optimizer(Ipopt.Optimizer()), + ) + MOI.set( + model, + MathOptComplements.DefaultComplementarityReformulation(), + relax, + ) @test_throws Exception MOI.Utilities.attach_optimizer(model) end end @testset "Solve Fletcher-Leyffer Ex1 problem" begin model = Instances.fletcher_leyffer_ex1_model() - JuMP.set_optimizer(model, () -> MathOptComplements.Optimizer(Ipopt.Optimizer())) - MOI.set(model, MathOptComplements.DefaultComplementarityReformulation(), relax) + JuMP.set_optimizer( + model, + () -> MathOptComplements.Optimizer(Ipopt.Optimizer()), + ) + MOI.set( + model, + MathOptComplements.DefaultComplementarityReformulation(), + relax, + ) JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0) JuMP.set_silent(model) JuMP.optimize!(model) @@ -281,8 +314,15 @@ end @testset "Solve NCP problem $(func)" for func in [simple_ncp, simple_lp_3] model, vars, sol = func() - JuMP.set_optimizer(model, () -> MathOptComplements.Optimizer(Ipopt.Optimizer())) - MOI.set(model, MathOptComplements.DefaultComplementarityReformulation(), relax) + JuMP.set_optimizer( + model, + () -> MathOptComplements.Optimizer(Ipopt.Optimizer()), + ) + MOI.set( + model, + MathOptComplements.DefaultComplementarityReformulation(), + relax, + ) JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0) JuMP.set_silent(model) JuMP.optimize!(model) @@ -306,7 +346,11 @@ end MOI.instantiate(Ipopt.Optimizer, with_cache_type = Float64), ), ) - MOI.set(model, MathOptComplements.DefaultComplementarityReformulation(), relax) + MOI.set( + model, + MathOptComplements.DefaultComplementarityReformulation(), + relax, + ) JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0) JuMP.set_silent(model) JuMP.optimize!(model) @@ -320,8 +364,14 @@ end G = MOI.ScalarQuadraticFunction{Float64} F = MOI.ScalarNonlinearFunction end - @test MOI.get(inner, MOI.NumberOfConstraints{F,MOI.LessThan{Float64}}()) > 0 - @test MOI.get(inner, MOI.NumberOfConstraints{G,MOI.LessThan{Float64}}()) == 0 + @test MOI.get( + inner, + MOI.NumberOfConstraints{F,MOI.LessThan{Float64}}(), + ) > 0 + @test MOI.get( + inner, + MOI.NumberOfConstraints{G,MOI.LessThan{Float64}}(), + ) == 0 @test JuMP.is_solved_and_feasible(model) @test JuMP.value.(vars) ≈ sol atol=1e-7 @@ -369,7 +419,8 @@ end @test MOI.supports_add_constrained_variables(b, S) @test !MOI.Bridges.is_variable_bridged(b, S) bridge_type = MOI.Bridges.Constraint.concrete_bridge_type(b, F, S) - @test bridge_type == MathOptComplements.Bridges.SpecifySetTypeBridge{Float64} + @test bridge_type == + MathOptComplements.Bridges.SpecifySetTypeBridge{Float64} @test MOI.supports(b, attr, bridge_type) @test MOI.supports( JuMP.unsafe_backend(model), @@ -386,8 +437,11 @@ end MathOptComplements.ComplementarityReformulation(), MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.Complements}, ) - @test MOI.get(model, MathOptComplements.ComplementarityReformulation(), c1) isa - MathOptComplements.FischerBurmeisterRelaxation + @test MOI.get( + model, + MathOptComplements.ComplementarityReformulation(), + c1, + ) isa MathOptComplements.FischerBurmeisterRelaxation JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0) JuMP.set_silent(model) JuMP.optimize!(model) @@ -396,13 +450,24 @@ end lazy = JuMP.backend(model).optimizer @test !MOI.Bridges.is_bridged(lazy, S) ci_mapped = first( - MOI.get(lazy, MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.Complements}()), + MOI.get( + lazy, + MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.Complements}(), + ), + ) + @test MOI.get( + lazy, + MathOptComplements.ComplementarityReformulation(), + ci_mapped, + ) isa MathOptComplements.FischerBurmeisterRelaxation + @test MOI.get( + model, + MathOptComplements.ComplementarityReformulation(), + c1, + ) isa MathOptComplements.FischerBurmeisterRelaxation + @test isnothing( + MOI.get(model, MathOptComplements.ComplementarityReformulation(), c2), ) - @test MOI.get(lazy, MathOptComplements.ComplementarityReformulation(), ci_mapped) isa - MathOptComplements.FischerBurmeisterRelaxation - @test MOI.get(model, MathOptComplements.ComplementarityReformulation(), c1) isa - MathOptComplements.FischerBurmeisterRelaxation - @test isnothing(MOI.get(model, MathOptComplements.ComplementarityReformulation(), c2)) end @testset "Per-constraint reformulation after optimize!" begin @@ -443,15 +508,21 @@ end c, MathOptComplements.FischerBurmeisterRelaxation(1e-8), ) - @test MOI.get(model, MathOptComplements.ComplementarityReformulation(), c) isa - MathOptComplements.FischerBurmeisterRelaxation + @test MOI.get( + model, + MathOptComplements.ComplementarityReformulation(), + c, + ) isa MathOptComplements.FischerBurmeisterRelaxation JuMP.optimize!(model) @test JuMP.is_solved_and_feasible(model) inner = backend(model).optimizer.model.optimizer.model # FB produces a nonlinear constraint @test MOI.get( inner, - MOI.NumberOfConstraints{MOI.ScalarNonlinearFunction,MOI.LessThan{Float64}}(), + MOI.NumberOfConstraints{ + MOI.ScalarNonlinearFunction, + MOI.LessThan{Float64}, + }(), ) == 1 end @@ -473,8 +544,11 @@ end MOI.VectorAffineFunction{Float64}, S, ) - @test MOI.Bridges.bridge_type(opt_nlp, MOI.VectorAffineFunction{Float64}, S) == - MathOptComplements.Bridges.NonlinearBridge{Float64,MOI.Nonnegatives} + @test MOI.Bridges.bridge_type( + opt_nlp, + MOI.VectorAffineFunction{Float64}, + S, + ) == MathOptComplements.Bridges.NonlinearBridge{Float64,MOI.Nonnegatives} # SOS1 path: VectorOfVariables in ComplementsWithSetType{Zeros} → ToSOS1Bridge opt_sos1 = MathOptComplements.Optimizer( MOI.Bridges.full_bridge_optimizer(HiGHS.Optimizer(), Float64), @@ -487,9 +561,17 @@ end @testset "ComplementarityReformulation supports on bridge types" begin model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) attr = MathOptComplements.ComplementarityReformulation() - @test MOI.supports(model, attr, MathOptComplements.Bridges.SplitIntervalBridge) + @test MOI.supports( + model, + attr, + MathOptComplements.Bridges.SplitIntervalBridge, + ) @test MOI.supports(model, attr, MathOptComplements.Bridges.FlipSignBridge) - @test MOI.supports(model, attr, MathOptComplements.Bridges.ComplementsVectorizeBridge) + @test MOI.supports( + model, + attr, + MathOptComplements.Bridges.ComplementsVectorizeBridge, + ) @test MOI.supports(model, attr, MathOptComplements.Bridges.NonlinearBridge) end @@ -525,12 +607,20 @@ end ) > 0 # Change to FB and re-reformulate → nonlinear constraints attr = MathOptComplements.ComplementarityReformulation() - MOI.set(model, attr, ci, MathOptComplements.FischerBurmeisterRelaxation(1e-8)) + MOI.set( + model, + attr, + ci, + MathOptComplements.FischerBurmeisterRelaxation(1e-8), + ) MOI.Bridges.final_touch(model) MOI.Bridges.final_touch(nl_model) @test MOI.get( inner, - MOI.NumberOfConstraints{MOI.ScalarNonlinearFunction,MOI.LessThan{Float64}}(), + MOI.NumberOfConstraints{ + MOI.ScalarNonlinearFunction, + MOI.LessThan{Float64}, + }(), ) > 0 # Test SplitIntervalBridge metadata b_split = MOI.Bridges.bridge(model, ci) @@ -538,13 +628,19 @@ end @test length( MOI.get( b_split, - MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.GreaterThan{Float64}}(), + MOI.ListOfConstraintIndices{ + MOI.VariableIndex, + MOI.GreaterThan{Float64}, + }(), ), ) == 1 @test length( MOI.get( b_split, - MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.LessThan{Float64}}(), + MOI.ListOfConstraintIndices{ + MOI.VariableIndex, + MOI.LessThan{Float64}, + }(), ), ) == 1 end @@ -568,8 +664,10 @@ end MOI.set(model, attr, ci, relax) # FlipSign flips Nonpositives → Nonnegatives, check child exists S = MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives} - @test MOI.get(inner, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},S}()) == - 1 + @test MOI.get( + inner, + MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},S}(), + ) == 1 # Check the attribute was propagated to the child constraint b = MOI.Bridges.bridge(model, ci) @test MOI.get(inner, attr, b.constraint) isa @@ -595,8 +693,10 @@ end MOI.set(model, attr, ci, relax) # ComplementsVectorize shifts GreaterThan → Nonnegatives, check child exists S = MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives} - @test MOI.get(inner, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},S}()) == - 1 + @test MOI.get( + inner, + MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},S}(), + ) == 1 # Check the attribute was propagated to the child constraint b = MOI.Bridges.bridge(model, ci) @test MOI.get(inner, attr, b.constraint) isa @@ -616,7 +716,10 @@ end MOI.Utilities.attach_optimizer(model) # ComplementsWithSetType is bridged further to nonlinear constraints S = MathOptComplements.ComplementsWithSetType{MOI.Nonnegatives} - @test MOI.get(inner, MOI.NumberOfConstraints{MOI.VectorOfVariables,S}()) == 0 + @test MOI.get( + inner, + MOI.NumberOfConstraints{MOI.VectorOfVariables,S}(), + ) == 0 end @testset "Lower bound (GreaterThan)" begin @@ -628,7 +731,10 @@ end set_optimizer(model, () -> make_opt(inner)) MOI.Utilities.attach_optimizer(model) S = MathOptComplements.ComplementsWithSetType{MOI.GreaterThan{Float64}} - @test MOI.get(inner, MOI.NumberOfConstraints{MOI.VectorOfVariables,S}()) == 0 + @test MOI.get( + inner, + MOI.NumberOfConstraints{MOI.VectorOfVariables,S}(), + ) == 0 end @testset "Upper bound (LessThan)" begin @@ -640,7 +746,10 @@ end set_optimizer(model, () -> make_opt(inner)) MOI.Utilities.attach_optimizer(model) S = MathOptComplements.ComplementsWithSetType{MOI.LessThan{Float64}} - @test MOI.get(inner, MOI.NumberOfConstraints{MOI.VectorOfVariables,S}()) == 0 + @test MOI.get( + inner, + MOI.NumberOfConstraints{MOI.VectorOfVariables,S}(), + ) == 0 end @testset "Range case (x1 unbounded → Interval)" begin @@ -652,7 +761,10 @@ end set_optimizer(model, () -> make_opt(inner)) MOI.Utilities.attach_optimizer(model) S = MathOptComplements.ComplementsWithSetType{MOI.Interval{Float64}} - @test MOI.get(inner, MOI.NumberOfConstraints{MOI.VectorOfVariables,S}()) == 0 + @test MOI.get( + inner, + MOI.NumberOfConstraints{MOI.VectorOfVariables,S}(), + ) == 0 end @testset "Range case (x1 bounded nonneg)" begin @@ -664,7 +776,10 @@ end set_optimizer(model, () -> make_opt(inner)) MOI.Utilities.attach_optimizer(model) S = MathOptComplements.ComplementsWithSetType{MOI.Interval{Float64}} - @test MOI.get(inner, MOI.NumberOfConstraints{MOI.VectorOfVariables,S}()) == 0 + @test MOI.get( + inner, + MOI.NumberOfConstraints{MOI.VectorOfVariables,S}(), + ) == 0 end end @@ -702,7 +817,11 @@ end # Create a model with an Interval complementarity constraint x = MOI.add_variable(opt) y, _ = MOI.add_constrained_variable(opt, MOI.Interval(0.0, 1.0)) - ci = MOI.add_constraint(opt, MOI.VectorOfVariables([x, y]), MOI.Complements(2)) + ci = MOI.add_constraint( + opt, + MOI.VectorOfVariables([x, y]), + MOI.Complements(2), + ) MOI.Bridges.final_touch(opt) # Step 1: Complements → SpecifySetTypeBridge