Skip to content

Commit 288549a

Browse files
authored
add geomean duals tests and geomean to entropy bridge (#1178)
* add bridge for geomean to relative entropy; add duals for geomean1 test * don't test duals in geomean bridge * fix NEWS entry; address @blegat comments * add duals for geomean2 and geomean3 * fix duals * use geomean 2 and 3 tests for new bridge also * add bridge for geomean to relative entropy; add duals for geomean1 test * don't test duals in geomean bridge * fix NEWS entry; address @blegat comments * add duals for geomean2 and geomean3 * fix duals * use geomean 2 and 3 tests for new bridge also * update entropy cone dual defn; fix issues with geom to entr bridge and add more tests * update comments
1 parent 7543afe commit 288549a

12 files changed

Lines changed: 464 additions & 37 deletions

File tree

NEWS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
MathOptInterface (MOI) release notes
22
====================================
33

4+
- Added `GeoMeantoRelEntrBridge` to bridge a geomean constraint to a relative entropy constraint.
5+
46
v0.9.17 (September 21, 2020)
57
---------------------------
68

@@ -122,7 +124,7 @@ v0.9.10 (January 31, 2020)
122124
- Added `dual_set_type` (#1002).
123125
- Added tests for vector specialized version of `delete` (#989, #1011).
124126
- Added PSD3 test (#1007).
125-
- Clarifed dual solution of `Tests.pow1v` and `Tests.pow1f` (#1013).
127+
- Clarified dual solution of `Tests.pow1v` and `Tests.pow1f` (#1013).
126128
- Added support for `EqualTo` and `Zero` in
127129
`Bridges.Constraint.SplitIntervalBridge` (#1005).
128130
- Fixed `Utilities.vectorize` for empty vector (#1003).

docs/src/apireference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,7 @@ Bridges.Constraint.SOCtoNonConvexQuadBridge
817817
Bridges.Constraint.RSOCtoNonConvexQuadBridge
818818
Bridges.Constraint.NormInfinityBridge
819819
Bridges.Constraint.NormOneBridge
820+
Bridges.Constraint.GeoMeantoRelEntrBridge
820821
Bridges.Constraint.GeoMeanBridge
821822
Bridges.Constraint.RelativeEntropyBridge
822823
Bridges.Constraint.NormSpectralBridge

src/Bridges/Constraint/Constraint.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ const RSOCtoNonConvexQuad{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoNo
4949
include("norm_to_lp.jl")
5050
const NormInfinity{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T}, OT}
5151
const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT}
52+
include("geomean_to_relentr.jl")
53+
const GeoMeantoRelEntr{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeantoRelEntrBridge{T}, OT}
5254
include("geomean.jl")
5355
const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT}
5456
include("relentr_to_exp.jl")
@@ -96,6 +98,7 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
9698
# interpretation).
9799
MOIB.add_bridge(bridged_model, NormInfinityBridge{T})
98100
MOIB.add_bridge(bridged_model, NormOneBridge{T})
101+
MOIB.add_bridge(bridged_model, GeoMeantoRelEntrBridge{T})
99102
MOIB.add_bridge(bridged_model, GeoMeanBridge{T})
100103
MOIB.add_bridge(bridged_model, RelativeEntropyBridge{T})
101104
MOIB.add_bridge(bridged_model, NormSpectralBridge{T})
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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

src/Bridges/Constraint/relentr_to_exp.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ end
3737
# Attributes, Bridge acting as a model
3838
MOI.get(bridge::RelativeEntropyBridge, ::MOI.NumberOfVariables) = length(bridge.y)
3939
MOI.get(bridge::RelativeEntropyBridge, ::MOI.ListOfVariableIndices) = bridge.y
40-
MOI.get(bridge::RelativeEntropyBridge{T, F, G, H}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F, G, H} = 1
41-
MOI.get(bridge::RelativeEntropyBridge{T, F, G, H}, ::MOI.NumberOfConstraints{G, MOI.ExponentialCone}) where {T, F, G, H} = length(bridge.y)
42-
MOI.get(bridge::RelativeEntropyBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F, G, H} = [bridge.ge_index]
43-
MOI.get(bridge::RelativeEntropyBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{G, MOI.ExponentialCone}) where {T, F, G, H} = bridge.exp_indices
40+
MOI.get(bridge::RelativeEntropyBridge{T, F}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F} = 1
41+
MOI.get(bridge::RelativeEntropyBridge{T, F, G}, ::MOI.NumberOfConstraints{G, MOI.ExponentialCone}) where {T, F, G} = length(bridge.y)
42+
MOI.get(bridge::RelativeEntropyBridge{T, F}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F} = [bridge.ge_index]
43+
MOI.get(bridge::RelativeEntropyBridge{T, F, G}, ::MOI.ListOfConstraintIndices{G, MOI.ExponentialCone}) where {T, F, G} = bridge.exp_indices
4444

4545
# References
4646
function MOI.delete(model::MOI.ModelLike, bridge::RelativeEntropyBridge)
@@ -105,14 +105,15 @@ function MOI.get(model::MOI.ModelLike, attr::Union{MOI.ConstraintDual, MOI.Const
105105
end
106106
# Given constraint dual start of (u, v, w), constraint dual on GreaterThan is u
107107
# and on exponential cone constraint i is (r_i, w_i, v_i), but since y_i is free,
108-
# its dual is 0, so we have -r_i + value[1] == 0 hence r_i = value[1].
108+
# its dual is 0, so we have -r_i + u == 0 hence r_i = u.
109109
# Note: alternatively, we could use the Lambert W function to calculate
110110
# r_i = exp(W(-w_i / (-v_i * e))) * (-v_i * e), but this is more complicated.
111111
function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintDualStart, bridge::RelativeEntropyBridge, value)
112-
MOI.set(model, MOI.ConstraintDualStart(), bridge.ge_index, value[1])
112+
u = value[1]
113+
MOI.set(model, MOI.ConstraintDualStart(), bridge.ge_index, u)
113114
w_start = 1 + length(bridge.y)
114115
for i in eachindex(bridge.y)
115-
MOI.set(model, MOI.ConstraintDualStart(), bridge.exp_indices[i], [value[1], value[w_start + i], value[1 + i]])
116+
MOI.set(model, MOI.ConstraintDualStart(), bridge.exp_indices[i], [u, value[w_start + i], value[1 + i]])
116117
end
117118
return
118119
end

src/Test/contconic.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,11 @@ function _geomean1test(model::MOI.ModelLike, config::TestConfig, vecofvars, n=3)
14101410
@test MOI.get(model, MOI.ConstraintPrimal(), gmc) ones(n+1) atol=atol rtol=rtol
14111411

14121412
@test MOI.get(model, MOI.ConstraintPrimal(), c) n atol=atol rtol=rtol
1413+
1414+
if config.duals
1415+
@test MOI.get(model, MOI.ConstraintDual(), gmc) vcat(-1.0, fill(inv(n), n)) atol=atol rtol=rtol
1416+
@test MOI.get(model, MOI.ConstraintDual(), c) -inv(n) atol=atol rtol=rtol
1417+
end
14131418
end
14141419
end
14151420

@@ -1424,7 +1429,7 @@ function _geomean2test(model::MOI.ModelLike, config::TestConfig, vecofvars)
14241429
# max t
14251430
# st (t,x_1,x_2,...,x_9) ∈ GeometricMeanCone(10)
14261431
# x_1 == x_2, ..., x_9 == 1
1427-
# the optimal solution is 1
1432+
# the optimal solution is 1 with optimal value 1
14281433

14291434
@test MOIU.supports_default_copy_to(model, #=copy_names=# false)
14301435
@test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}())
@@ -1475,6 +1480,11 @@ function _geomean2test(model::MOI.ModelLike, config::TestConfig, vecofvars)
14751480

14761481
@test MOI.get(model, MOI.ConstraintPrimal(), gmc) ones(n + 1) atol=atol rtol=rtol
14771482
@test MOI.get(model, MOI.ConstraintPrimal(), cx) ones(n) atol=atol rtol=rtol
1483+
1484+
if config.duals
1485+
@test MOI.get(model, MOI.ConstraintDual(), gmc) vcat(-1, fill(inv(n), n)) atol=atol rtol=rtol
1486+
@test MOI.get(model, MOI.ConstraintDual(), cx) fill(-inv(n), n) atol=atol rtol=rtol
1487+
end
14781488
end
14791489
end
14801490

@@ -1489,7 +1499,7 @@ function _geomean3test(model::MOI.ModelLike, config::TestConfig, vecofvars)
14891499
# max 2t
14901500
# st (t,x) ∈ GeometricMeanCone(2)
14911501
# x <= 2
1492-
# the optimal solution is 4
1502+
# the optimal solution is (t, x) = (2, 2) with objective value 4
14931503

14941504
@test MOIU.supports_default_copy_to(model, #=copy_names=# false)
14951505
@test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}())
@@ -1535,6 +1545,11 @@ function _geomean3test(model::MOI.ModelLike, config::TestConfig, vecofvars)
15351545

15361546
@test MOI.get(model, MOI.ConstraintPrimal(), gmc) [2.0; 2.0] atol=atol rtol=rtol
15371547
@test MOI.get(model, MOI.ConstraintPrimal(), cx) 2.0 atol=atol rtol=rtol
1548+
1549+
if config.duals
1550+
@test MOI.get(model, MOI.ConstraintDual(), gmc) [-2.0, 2.0] atol=atol rtol=rtol
1551+
@test MOI.get(model, MOI.ConstraintDual(), cx) -2.0 atol=atol rtol=rtol
1552+
end
15381553
end
15391554
end
15401555

src/sets.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,7 @@ The relative entropy cone ``\\{ (u, v, w) \\in \\mathbb{R}^{1+2n} : u \\ge \\sum
335335
### Duality note
336336
337337
The dual of the relative entropy cone is
338-
``\\{ (u, v, w) \\in \\mathbb{R}^{1+2n} : \\forall i, -u \\exp (w_i/u) \\le \\exp(1) v_i, u < 0 \\}`` of dimension `dimension```{}=2n+1``.
339-
Note that the inequality is rewritten in terms of ``\\log`` as follows: ``w_i \\le u (\\log(v_i/-u) + 1)``.
338+
``\\{ (u, v, w) \\in \\mathbb{R}^{1+2n} : \\forall i, w_i \\ge u (\\log (\\frac{u}{v_i}) - 1), v_i \\ge 0, u > 0 \\}`` of dimension `dimension```{}=2n+1``.
340339
"""
341340
struct RelativeEntropyCone <: AbstractVectorSet
342341
dimension::Int

test/Bridges/Constraint/geomean.jl

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,24 @@ const MOIB = MathOptInterface.Bridges
88

99
include("../utilities.jl")
1010

11-
mock = MOIU.MockOptimizer(MOIU.Model{Float64}())
11+
mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}()))
1212
config = MOIT.TestConfig()
1313

1414
@testset "GeoMean" begin
1515
bridged_mock = MOIB.Constraint.GeoMean{Float64}(mock)
1616

17-
MOIT.basic_constraint_tests(
18-
bridged_mock, config,
19-
include = [(F, S)
20-
for F in [MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64},
21-
MOI.VectorQuadraticFunction{Float64}]
22-
for S in [MOI.GeometricMeanCone]])
17+
MOIT.basic_constraint_tests(bridged_mock, config,
18+
include = [(F, MOI.GeometricMeanCone) for F in [
19+
MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64}, MOI.VectorQuadraticFunction{Float64}
20+
]])
2321

2422
@testset "geomean1test" begin
25-
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [ones(4); 2; 2; 2])
23+
var_primal = vcat(ones(4), 2, 2, 2)
24+
rsoc_duals = [[2, 2, -2] / 3, [1, 1, -√2] / 3, [1, 1, -√2] / 3]
25+
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, var_primal,
26+
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1, -inv(3)],
27+
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => rsoc_duals)
28+
2629
MOIT.geomean1vtest(bridged_mock, config)
2730
MOIT.geomean1ftest(bridged_mock, config)
2831

@@ -37,7 +40,7 @@ config = MOIT.TestConfig()
3740
MOI.set.(mock, MOI.ConstraintName(), rsoc, ["rsoc21", "rsoc11", "rsoc12"])
3841

3942
s = """
40-
variables: t, x, y, z, x11, x12, x21
43+
variables: t, x, y, z, x21, x11, x12
4144
lessthan1: t + -0.5 * x21 in MathOptInterface.LessThan(0.0)
4245
lessthan2: x + y + z in MathOptInterface.LessThan(3.0)
4346
rsoc11: [1.0x, y, x11] in MathOptInterface.RotatedSecondOrderCone(3)
@@ -71,15 +74,25 @@ config = MOIT.TestConfig()
7174
MOIU.test_models_equal(bridged_mock, model, var_names, ["lessthan", "geomean"])
7275
end
7376

74-
# Dual is not yet implemented for GeoMean bridge
77+
# set primal/dual start is not yet implemented for GeoMean bridge
7578
ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.GeometricMeanCone}()))
7679
test_delete_bridge(bridged_mock, ci, 4, ((MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone, 0),
7780
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, 1),
7881
(MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0)))
7982
end
8083

8184
@testset "geomean2test" begin
82-
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, vcat(ones(10), 4.0, fill(2 * 2, 2), fill(2, 4), fill(2, 8)))
85+
var_primal = vcat(ones(10), 4.0, fill(2 * 2, 2), fill(2, 4), fill(2, 8))
86+
rsoc_duals = vcat(
87+
[[1/√2, 1/√2, -1] * 4/9],
88+
fill([1, 1, -√2] * 2/9, 2),
89+
fill([1/√2, 1/√2, -1] * 2/9, 4),
90+
fill([1, 1, -√2] * 1/9, 8))
91+
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, var_primal,
92+
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1],
93+
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => fill(-inv(9), 9),
94+
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => rsoc_duals)
95+
8396
MOIT.geomean2vtest(bridged_mock, config)
8497
MOIT.geomean2ftest(bridged_mock, config)
8598

@@ -167,15 +180,18 @@ config = MOIT.TestConfig()
167180
MOIU.test_models_equal(bridged_mock, model, var_names, vcat(equalto_names, "geomean"))
168181
end
169182

170-
# Dual is not yet implemented for GeoMean bridge
183+
# set primal/dual start is not yet implemented for GeoMean bridge
171184
ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.GeometricMeanCone}()))
172185
test_delete_bridge(bridged_mock, ci, 10, ((MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone, 0),
173186
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, 0),
174187
(MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0)))
175188
end
176189

177190
@testset "geomean3test" begin
178-
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, 2 * ones(2))
191+
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2, 2],
192+
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-2, -2],
193+
(MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0]])
194+
179195
MOIT.geomean3vtest(bridged_mock, config)
180196
MOIT.geomean3ftest(bridged_mock, config)
181197

@@ -222,11 +238,10 @@ config = MOIT.TestConfig()
222238
MOIU.test_models_equal(bridged_mock, model, ["t", "x"], ["lessthan", "geomean"])
223239
end
224240

225-
# Dual is not yet implemented for GeoMean bridge
241+
# set primal/dual start is not yet implemented for GeoMean bridge
226242
ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.GeometricMeanCone}()))
227243
test_delete_bridge(bridged_mock, ci, 2, ((MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone, 0),
228244
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, 1),
229245
(MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0)))
230246
end
231-
232247
end

0 commit comments

Comments
 (0)