|
| 1 | +abstract type AbstractSOCtoNonConvexQuadBridge{T} <: AbstractBridge end |
| 2 | + |
| 3 | +""" |
| 4 | + SOCtoNonConvexQuadBridge{T} |
| 5 | +
|
| 6 | +Constraints of the form `VectorOfVariables`-in-`SecondOrderCone` can be |
| 7 | +transformed into a `ScalarQuadraticFunction`-in-`LessThan` and a |
| 8 | +`ScalarAffineFunction`-in-`GreaterThan`. Indeed, the definition of the |
| 9 | +second-order cone |
| 10 | +```math |
| 11 | +t \\ge \\lVert x \\rVert_2 \\ (1) |
| 12 | +``` |
| 13 | +is equivalent to |
| 14 | +```math |
| 15 | +\\sum x_i^2 \\le t^2 (2) |
| 16 | +``` |
| 17 | +with ``t \\ge 0``. (3) |
| 18 | +
|
| 19 | +*WARNING* This transformation starts from a convex constraint (1) and creates a |
| 20 | +non-convex constraint (2), because the Q matrix associated with the constraint 2 |
| 21 | +has one negative eigenvalue. This might be wrongly interpreted by a solver. |
| 22 | +Some solvers can look at (2) and understand that it is a second order cone, but |
| 23 | +this is not a general rule. |
| 24 | +For these reasons this bridge is not automatically added by [`MOI.Bridges.full_bridge_optimizer`](@ref). |
| 25 | +Care is recommended when adding this bridge to a optimizer. |
| 26 | +""" |
| 27 | +struct SOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} |
| 28 | + quad::CI{MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}} |
| 29 | + var_pos::Vector{CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}} |
| 30 | + vars::Vector{MOI.VariableIndex} |
| 31 | +end |
| 32 | +function bridge_constraint(::Type{SOCtoNonConvexQuadBridge{T}}, model, |
| 33 | + func::MOI.VectorOfVariables, |
| 34 | + set::MOI.SecondOrderCone) where T |
| 35 | + |
| 36 | + vis = func.variables |
| 37 | + |
| 38 | + t = vis[1] |
| 39 | + x = vis[2:end] |
| 40 | + a_terms = MOI.ScalarAffineTerm{T}[] |
| 41 | + q_terms = MOI.ScalarQuadraticTerm{T}[] |
| 42 | + push!(q_terms, MOI.ScalarQuadraticTerm(-T(2), t, t)) |
| 43 | + for var in x |
| 44 | + push!(q_terms, MOI.ScalarQuadraticTerm(T(2), var, var)) |
| 45 | + end |
| 46 | + |
| 47 | + fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) |
| 48 | + quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) |
| 49 | + # ScalarAffineFunction's are added instead of SingleVariable's |
| 50 | + # because models can only have one SingleVariable per variable. |
| 51 | + # Hence, adding a SingleVariable constraint here could conflict with |
| 52 | + # a user defined SingleVariable |
| 53 | + fp = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(t)) |
| 54 | + var_pos = MOI.add_constraint(model, fp, MOI.GreaterThan(zero(T))) |
| 55 | + |
| 56 | + return SOCtoNonConvexQuadBridge(quad, [var_pos], vis) |
| 57 | +end |
| 58 | + |
| 59 | +""" |
| 60 | + RSOCtoNonConvexQuadBridge{T} |
| 61 | +
|
| 62 | +Constraints of the form `VectorOfVariables`-in-`SecondOrderCone` can be |
| 63 | +transformed into a `ScalarQuadraticFunction`-in-`LessThan` and a |
| 64 | +`ScalarAffineFunction`-in-`GreaterThan`. Indeed, the definition of the |
| 65 | +second-order cone |
| 66 | +```math |
| 67 | +2tu \\ge \\lVert x \\rVert_2^2, t,u \\ge 0 (1) |
| 68 | +``` |
| 69 | +is equivalent to |
| 70 | +```math |
| 71 | +\\sum x_i^2 \\le 2tu (2) |
| 72 | +``` |
| 73 | +with ``t,u \\ge 0``. (3) |
| 74 | +
|
| 75 | +*WARNING* This transformation starts from a convex constraint (1) and creates a |
| 76 | +non-convex constraint (2), because the Q matrix associated with the constraint 2 |
| 77 | +has two negative eigenvalues. This might be wrongly interpreted by a solver. |
| 78 | +Some solvers can look at (2) and understand that it is a rotated second order cone, but |
| 79 | +this is not a general rule. |
| 80 | +For these reasons, this bridge is not automatically added by [`MOI.Bridges.full_bridge_optimizer`](@ref). |
| 81 | +Care is recommended when adding this bridge to an optimizer. |
| 82 | +""" |
| 83 | +struct RSOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} |
| 84 | + quad::CI{MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}} |
| 85 | + var_pos::Vector{CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}} |
| 86 | + vars::Vector{MOI.VariableIndex} |
| 87 | +end |
| 88 | +function bridge_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, model, |
| 89 | + func::MOI.VectorOfVariables, |
| 90 | + set::MOI.RotatedSecondOrderCone) where T |
| 91 | + |
| 92 | + vis = func.variables |
| 93 | + |
| 94 | + t = vis[1] |
| 95 | + u = vis[2] |
| 96 | + x = vis[3:end] |
| 97 | + a_terms = MOI.ScalarAffineTerm{T}[] |
| 98 | + q_terms = MOI.ScalarQuadraticTerm{T}[] |
| 99 | + push!(q_terms, MOI.ScalarQuadraticTerm(-T(2), t, u)) |
| 100 | + for var in x |
| 101 | + push!(q_terms, MOI.ScalarQuadraticTerm(T(2), var, var)) |
| 102 | + end |
| 103 | + |
| 104 | + fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) |
| 105 | + quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) |
| 106 | + # ScalarAffineFunction's are added instead of SingleVariable's |
| 107 | + # because models can only have one SingleVariable per variable. |
| 108 | + # Hence, adding a SingleVariable constraint here could conflict with |
| 109 | + # a user defined SingleVariable |
| 110 | + fp1 = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(t)) |
| 111 | + var_pos1 = MOI.add_constraint(model, fp1, MOI.GreaterThan(zero(T))) |
| 112 | + fp2 = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(u)) |
| 113 | + var_pos2 = MOI.add_constraint(model, fp2, MOI.GreaterThan(zero(T))) |
| 114 | + |
| 115 | + return RSOCtoNonConvexQuadBridge(quad, [var_pos1, var_pos2], vis) |
| 116 | +end |
| 117 | + |
| 118 | +function MOI.supports_constraint(::Type{SOCtoNonConvexQuadBridge{T}}, |
| 119 | + ::Type{MOI.VectorOfVariables}, |
| 120 | + ::Type{MOI.SecondOrderCone}) where T |
| 121 | + return true |
| 122 | +end |
| 123 | +function MOI.supports_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, |
| 124 | + ::Type{MOI.VectorOfVariables}, |
| 125 | + ::Type{MOI.RotatedSecondOrderCone}) where T |
| 126 | + return true |
| 127 | +end |
| 128 | + |
| 129 | +MOIB.added_constrained_variable_types(::Type{<:AbstractSOCtoNonConvexQuadBridge}) = Tuple{DataType}[] |
| 130 | +function MOIB.added_constraint_types(::Type{<:AbstractSOCtoNonConvexQuadBridge{T}}) where T |
| 131 | + return [ |
| 132 | + (MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}), |
| 133 | + (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), |
| 134 | + ] |
| 135 | +end |
| 136 | + |
| 137 | +function concrete_bridge_type(::Type{SOCtoNonConvexQuadBridge{T}}, |
| 138 | + ::Type{MOI.VectorOfVariables}, |
| 139 | + ::Type{MOI.SecondOrderCone}) where T |
| 140 | + return SOCtoNonConvexQuadBridge{T} |
| 141 | +end |
| 142 | +function concrete_bridge_type(::Type{RSOCtoNonConvexQuadBridge{T}}, |
| 143 | + ::Type{MOI.VectorOfVariables}, |
| 144 | + ::Type{MOI.RotatedSecondOrderCone}) where T |
| 145 | + return RSOCtoNonConvexQuadBridge{T} |
| 146 | +end |
| 147 | + |
| 148 | +# Attributes, Bridge acting as a model |
| 149 | +function MOI.get(::AbstractSOCtoNonConvexQuadBridge{T}, |
| 150 | + ::MOI.NumberOfConstraints{MOI.ScalarQuadraticFunction{T}, |
| 151 | + MOI.LessThan{T}}) where T |
| 152 | + return 1 |
| 153 | +end |
| 154 | + |
| 155 | +function MOI.get(bridge::AbstractSOCtoNonConvexQuadBridge{T}, |
| 156 | + ::MOI.ListOfConstraintIndices{ |
| 157 | + MOI.ScalarQuadraticFunction{T}, |
| 158 | + MOI.LessThan{T}}) where T |
| 159 | + return [bridge.quad] |
| 160 | +end |
| 161 | + |
| 162 | +function MOI.get(bridge::AbstractSOCtoNonConvexQuadBridge{T}, |
| 163 | + ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, |
| 164 | + MOI.GreaterThan{T}}) where T |
| 165 | + return length(bridge.var_pos) |
| 166 | +end |
| 167 | + |
| 168 | +function MOI.get(bridge::AbstractSOCtoNonConvexQuadBridge{T}, |
| 169 | + ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, |
| 170 | + MOI.GreaterThan{T}}) where T |
| 171 | + return bridge.var_pos |
| 172 | +end |
| 173 | + |
| 174 | +# References |
| 175 | +function MOI.delete(model::MOI.ModelLike, bridge::AbstractSOCtoNonConvexQuadBridge) |
| 176 | + MOI.delete(model, bridge.quad) |
| 177 | + MOI.delete.(model, bridge.var_pos) |
| 178 | +end |
| 179 | + |
| 180 | +# Attributes, Bridge acting as a constraint |
| 181 | +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, |
| 182 | + bridge::AbstractSOCtoNonConvexQuadBridge) |
| 183 | + vals = MOI.get.(model, MOI.VariablePrimal(attr.N), bridge.vars) |
| 184 | + return vals |
| 185 | +end |
| 186 | + |
| 187 | +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, |
| 188 | + b::SOCtoNonConvexQuadBridge{T}) where T |
| 189 | + return MOI.SecondOrderCone(length(b.vars)) |
| 190 | +end |
| 191 | +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, |
| 192 | + b::RSOCtoNonConvexQuadBridge{T}) where T |
| 193 | + return MOI.RotatedSecondOrderCone(length(b.vars)) |
| 194 | +end |
| 195 | + |
| 196 | +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction, |
| 197 | + b::AbstractSOCtoNonConvexQuadBridge{T}) where T |
| 198 | + return MOI.VectorOfVariables(b.vars) |
| 199 | +end |
0 commit comments