|
| 1 | +""" |
| 2 | + GeoMeantoRelEntrBridge{T} |
| 3 | +
|
| 4 | +The `geometric mean cone` is representable with a relative entropy constraint and a |
| 5 | +nonnegative auxiliary variable, since ``u \\le \\prod_{i=1}^n w_i^{1/n}`` is equivalent |
| 6 | +to ``y \\ge 0`` and ``0 \\le u + y \\le \\prod_{i=1}^n w_i^{1/n}``, and the latter |
| 7 | +inequality is equivalent to ``1 \\le \\prod_{i=1}^n (\\frac{w_i}{u + y})^{1/n}``, which |
| 8 | +is equivalent to ``0 \\le \\sum_{i=1}^n \\log (\\frac{w_i}{u + y})^{1/n}``, which is |
| 9 | +equivalent to ``0 \\ge \\sum_{i=1}^n (u + y) \\log (\\frac{u + y}{w_i})``. Thus |
| 10 | +``(u, w) \\in GeometricMeanCone(1 + n)`` is representable as ``y \\ge 0``, |
| 11 | +``(0, w, (u + y) e) \\in RelativeEntropyCone(1 + 2n)``, where ``e`` is a vector of ones. |
| 12 | +""" |
| 13 | +struct GeoMeantoRelEntrBridge{T, F, G, H} <: AbstractBridge |
| 14 | + y::MOI.VariableIndex |
| 15 | + nn_index::CI{F, MOI.Nonnegatives} # for y >= 0 |
| 16 | + relentr_index::CI{G, MOI.RelativeEntropyCone} |
| 17 | +end |
| 18 | +function bridge_constraint(::Type{GeoMeantoRelEntrBridge{T, F, G, H}}, model::MOI.ModelLike, f::H, s::MOI.GeometricMeanCone) where {T, F, G, H} |
| 19 | + f_scalars = MOIU.eachscalar(f) |
| 20 | + (y, nn_index) = MOI.add_constrained_variables(model, MOI.Nonnegatives(1)) |
| 21 | + w_func = MOIU.vectorize(fill(MOIU.operate(+, T, f_scalars[1], MOI.SingleVariable(y[1])), MOI.dimension(s) - 1)) |
| 22 | + relentr_func = MOIU.operate(vcat, T, zero(MOI.ScalarAffineFunction{Float64}), f_scalars[2:end], w_func) |
| 23 | + relentr_index = MOI.add_constraint(model, relentr_func, MOI.RelativeEntropyCone(MOI.output_dimension(relentr_func))) |
| 24 | + return GeoMeantoRelEntrBridge{T, F, G, H}(y[1], nn_index, relentr_index) |
| 25 | +end |
| 26 | + |
| 27 | +MOI.supports_constraint(::Type{<:GeoMeantoRelEntrBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.GeometricMeanCone}) where T = true |
| 28 | +MOIB.added_constrained_variable_types(::Type{<:GeoMeantoRelEntrBridge}) = [(MOI.Nonnegatives,)] |
| 29 | +MOIB.added_constraint_types(::Type{<:GeoMeantoRelEntrBridge{T, F, G}}) where {T, F, G} = [(G, MOI.RelativeEntropyCone)] |
| 30 | +function concrete_bridge_type(::Type{<:GeoMeantoRelEntrBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.GeometricMeanCone}) where T |
| 31 | + F = MOI.VectorOfVariables |
| 32 | + S = MOIU.scalar_type(H) |
| 33 | + G = MOIU.promote_operation(vcat, T, T, S, MOIU.promote_operation(+, T, S, MOI.SingleVariable)) |
| 34 | + return GeoMeantoRelEntrBridge{T, F, G, H} |
| 35 | +end |
| 36 | + |
| 37 | +# Attributes, Bridge acting as a model |
| 38 | +MOI.get(bridge::GeoMeantoRelEntrBridge, ::MOI.NumberOfVariables) = 1 |
| 39 | +MOI.get(bridge::GeoMeantoRelEntrBridge, ::MOI.ListOfVariableIndices) = [bridge.y] |
| 40 | +MOI.get(bridge::GeoMeantoRelEntrBridge{T, F}, ::MOI.NumberOfConstraints{F, MOI.Nonnegatives}) where {T, F} = 1 |
| 41 | +MOI.get(bridge::GeoMeantoRelEntrBridge{T, F, G}, ::MOI.NumberOfConstraints{G, MOI.RelativeEntropyCone}) where {T, F, G} = 1 |
| 42 | +MOI.get(bridge::GeoMeantoRelEntrBridge{T, F}, ::MOI.ListOfConstraintIndices{F, MOI.Nonnegatives}) where {T, F} = [bridge.nn_index] |
| 43 | +MOI.get(bridge::GeoMeantoRelEntrBridge{T, F, G}, ::MOI.ListOfConstraintIndices{G, MOI.RelativeEntropyCone}) where {T, F, G} = [bridge.relentr_index] |
| 44 | + |
| 45 | +# References |
| 46 | +function MOI.delete(model::MOI.ModelLike, bridge::GeoMeantoRelEntrBridge) |
| 47 | + MOI.delete(model, bridge.relentr_index) |
| 48 | + MOI.delete(model, bridge.nn_index) |
| 49 | + MOI.delete(model, bridge.y) |
| 50 | +end |
| 51 | + |
| 52 | +# Attributes, Bridge acting as a constraint |
| 53 | +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::GeoMeantoRelEntrBridge{T, F, G, H}) where {T, F, G, H} |
| 54 | + relentr_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), bridge.relentr_index)) |
| 55 | + d = div(length(relentr_func) - 1, 2) |
| 56 | + u_func = MOIU.remove_variable(MOIU.operate(-, T, relentr_func[end], MOI.SingleVariable(bridge.y)), bridge.y) |
| 57 | + w_func = relentr_func[2:(1 + d)] |
| 58 | + return MOIU.convert_approx(H, MOIU.operate(vcat, T, u_func, w_func)) |
| 59 | +end |
| 60 | +MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, bridge::GeoMeantoRelEntrBridge) = MOI.GeometricMeanCone(1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), bridge.relentr_index)) - 1, 2)) |
| 61 | +MOI.supports(::MOI.ModelLike, ::Union{MOI.ConstraintPrimalStart, MOI.ConstraintDualStart}, |
| 62 | + ::Type{<:GeoMeantoRelEntrBridge}) = true |
| 63 | +function MOI.get(model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal, MOI.ConstraintPrimalStart}, bridge::GeoMeantoRelEntrBridge) |
| 64 | + relentr_primal = MOI.get(model, attr, bridge.relentr_index) |
| 65 | + d = div(length(relentr_primal) - 1, 2) |
| 66 | + y_val = MOI.get(model, attr, bridge.nn_index)[1] |
| 67 | + u_val = sum(relentr_primal[(2 + d):end]) / d - y_val |
| 68 | + return vcat(u_val, relentr_primal[2:(1 + d)]) |
| 69 | +end |
| 70 | +function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, bridge::GeoMeantoRelEntrBridge, value) |
| 71 | + u_val = value[1] |
| 72 | + u_pos = -min(zero(u_val), u_val) |
| 73 | + d = length(value) - 1 |
| 74 | + MOI.set(model, MOI.VariablePrimalStart(), bridge.y, u_pos) |
| 75 | + MOI.set(model, attr, bridge.nn_index, [u_pos]) |
| 76 | + MOI.set(model, attr, bridge.relentr_index, vcat(0, value[2:end], fill(u_pos + u_val, d))) |
| 77 | + return |
| 78 | +end |
| 79 | +# Given a is dual on y >= 0 and (b, c, d) is dual on RelativeEntropyCone constraint, |
| 80 | +# dual on (u, w) in GeometricMeanCone is (-a, c). Note that sum(d) = -a, so we could |
| 81 | +# instead use (sum(d), c). |
| 82 | +function MOI.get(model::MOI.ModelLike, attr::Union{MOI.ConstraintDual, MOI.ConstraintDualStart}, bridge::GeoMeantoRelEntrBridge) |
| 83 | + u_dual = -MOI.get(model, attr, bridge.nn_index)[1] |
| 84 | + relentr_dual = MOI.get(model, attr, bridge.relentr_index) |
| 85 | + d = div(length(relentr_dual) - 1, 2) |
| 86 | + w_dual = relentr_dual[2:(d + 1)] |
| 87 | + return vcat(u_dual, w_dual) |
| 88 | +end |
| 89 | +# Given GeometricMeanCone constraint dual start of (u, w), constraint dual on y >= 0 is -u |
| 90 | +# and on RelativeEntropyCone constraint is (-u/n, w, u/n * (log.(w/geomean(w)) .+ 1)). |
| 91 | +# Note log.(w/geomean(w)) = log.(w) .- sum(log, w) / n. |
| 92 | +function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintDualStart, bridge::GeoMeantoRelEntrBridge, value) |
| 93 | + d = length(value) - 1 |
| 94 | + u = value[1] |
| 95 | + w = value[2:end] |
| 96 | + MOI.set(model, MOI.ConstraintDualStart(), bridge.nn_index, [-u]) |
| 97 | + relentr_dual = vcat(-u / d, value[2:end], u / d * (log.(w) .+ (1 - sum(log, w) / d))) |
| 98 | + MOI.set(model, MOI.ConstraintDualStart(), bridge.relentr_index, relentr_dual) |
| 99 | + return |
| 100 | +end |
0 commit comments