diff --git a/src/Bridges/Constraint/bridges/LogDetBridge.jl b/src/Bridges/Constraint/bridges/LogDetBridge.jl index b72714dfef..792f5b9039 100644 --- a/src/Bridges/Constraint/bridges/LogDetBridge.jl +++ b/src/Bridges/Constraint/bridges/LogDetBridge.jl @@ -287,6 +287,8 @@ function MOI.get( slog = MOI.get(model, attr, first(bridge.lcindex))::G u = MOI.Utilities.eachscalar(slog)[2] ssu = MOI.get(model, attr, bridge.tlindex)::H + tl_rhs = MOI.constant(MOI.get(model, MOI.ConstraintSet(), bridge.tlindex)) + ssu = MOI.Utilities.operate!(-, T, ssu, tl_rhs) taff = MOI.Utilities.remove_variable(ssu, bridge.l) SI = MOI.Utilities.scalar_type(I) t = MOI.Utilities.convert_approx(SI, taff) diff --git a/src/Bridges/Constraint/bridges/NormNuclearBridge.jl b/src/Bridges/Constraint/bridges/NormNuclearBridge.jl index 838b0a67d0..e57dfd53e1 100644 --- a/src/Bridges/Constraint/bridges/NormNuclearBridge.jl +++ b/src/Bridges/Constraint/bridges/NormNuclearBridge.jl @@ -419,6 +419,10 @@ function MOI.get( T(2), ), ) + # `normalize_and_add_constraint` may have moved a constant from the + # first scalar function into `bridge.ge_index`'s set; add it back. + rhs = MOI.constant(MOI.get(model, MOI.ConstraintSet(), bridge.ge_index)) + MOI.Utilities.operate!(-, T, t, rhs) t = MOI.Utilities.remove_variable( MOI.Utilities.remove_variable(t, bridge.U), bridge.V, diff --git a/src/Bridges/Constraint/bridges/RelativeEntropyBridge.jl b/src/Bridges/Constraint/bridges/RelativeEntropyBridge.jl index 9a6aa20d0b..2f5519475f 100644 --- a/src/Bridges/Constraint/bridges/RelativeEntropyBridge.jl +++ b/src/Bridges/Constraint/bridges/RelativeEntropyBridge.jl @@ -167,6 +167,10 @@ function MOI.get( exp_func_i[2], ) end + # `normalize_and_add_constraint` may have moved a constant from the + # first scalar function into `bridge.ge_index`'s set; add it back. + rhs = MOI.constant(MOI.get(model, MOI.ConstraintSet(), bridge.ge_index)) + MOI.Utilities.operate_output_index!(-, T, 1, func, rhs) f = MOI.Utilities.remove_variable(func, bridge.y) return MOI.Utilities.convert_approx(H, f) end diff --git a/src/Bridges/Constraint/bridges/SquareBridge.jl b/src/Bridges/Constraint/bridges/SquareBridge.jl index ec8ad70d70..fe3132a791 100644 --- a/src/Bridges/Constraint/bridges/SquareBridge.jl +++ b/src/Bridges/Constraint/bridges/SquareBridge.jl @@ -267,7 +267,7 @@ function MOI.get( f_ji = MOI.Utilities.operate(-, T, f[offset+i+(j-1)*dim], diff) # But we need to account for the constant moved into the set rhs = MOI.constant(MOI.get(model, MOI.ConstraintSet(), ci)) - f_ji = MOI.Utilities.operate!(-, T, f_ji, rhs) + f_ji = MOI.Utilities.operate!(+, T, f_ji, rhs) f[offset+j+(i-1)*dim] = MOI.Utilities.convert_approx(eltype(f), f_ji) end return MOI.Utilities.vectorize(f) @@ -320,7 +320,7 @@ function MOI.get( primal[offset+i+(j-1)*dim] = primal[offset+j+(i-1)*dim] = value[k] end for ((i, j), ci) in bridge.sym - primal[offset+i+(j-1)*dim] += MOI.get(model, attr, ci) + primal[offset+j+(i-1)*dim] -= MOI.get(model, attr, ci) end return primal end @@ -341,7 +341,7 @@ function MOI.set( k = offset for j in 1:dim, i in 1:j k += 1 - primal[k] = value[offset+j+(i-1)*dim] + primal[k] = value[offset+i+(j-1)*dim] end MOI.set(model, attr, bridge.triangle, primal) for ((i, j), ci) in bridge.sym diff --git a/test/Bridges/Constraint/test_LogDetBridge.jl b/test/Bridges/Constraint/test_LogDetBridge.jl index cf6afed7ca..1fe7e188dd 100644 --- a/test/Bridges/Constraint/test_LogDetBridge.jl +++ b/test/Bridges/Constraint/test_LogDetBridge.jl @@ -367,6 +367,36 @@ function test_runtests() return end +function test_runtests_constants() + MOI.Bridges.runtests( + MOI.Bridges.Constraint.LogDetBridge, + """ + variables: t, u, x11, x21, x22 + [t + 1.0, u + 2.0, x11 + 3.0, x21 + 4.0, x22 + 5.0] in LogDetConeTriangle(2) + """, + """ + variables: t, u, x11, x21, x22, v1, v2, v3, v4, v5 + [v4, u + 2.0, v1] in ExponentialCone() + [v5, u + 2.0, v3] in ExponentialCone() + t + -1.0 * v4 + -1.0 * v5 <= -1.0 + [x11 + 3.0, x21 + 4.0, x22 + 5.0, v1, v2, v1, 0, v3, 0, v3] in PositiveSemidefiniteConeTriangle(4) + """, + ) + MOI.Bridges.runtests( + MOI.Bridges.Constraint.RootDetBridge, + """ + variables: t, x11, x21, x22 + [t, x11, x21, x22] in RootDetConeTriangle(2) + """, + """ + variables: t, x11, x21, x22, v1, v2, v3 + [t, v1, v3] in GeometricMeanCone(3) + [x11, x21, x22, v1, v2, v1, 0, v3, 0, v3] in PositiveSemidefiniteConeTriangle(4) + """, + ) + return +end + end # module TestConstraintDet.runtests() diff --git a/test/Bridges/Constraint/test_NormSpectralBridge.jl b/test/Bridges/Constraint/test_NormSpectralBridge.jl index 8bf0f946d1..841b8c4bba 100644 --- a/test/Bridges/Constraint/test_NormSpectralBridge.jl +++ b/test/Bridges/Constraint/test_NormSpectralBridge.jl @@ -324,6 +324,33 @@ function test_runtests() return end +function test_runtests_constant() + MOI.Bridges.runtests( + MOI.Bridges.Constraint.NormSpectralBridge, + """ + variables: t, x11, x12, x21, x22 + [t + 1.0, x11 + 2.0, x21 + 3.0, x12 + 4.0, x22 + 5.0] in NormSpectralCone(2, 2) + """, + """ + variables: t, x11, x12, x21, x22 + [t + 1.0, 0, t + 1.0, x11 + 2.0, x12 + 4.0, t + 1.0, x21 + 3.0, x22 + 5.0, 0, t + 1.0] in PositiveSemidefiniteConeTriangle(4) + """, + ) + MOI.Bridges.runtests( + MOI.Bridges.Constraint.NormNuclearBridge, + """ + variables: t, x11, x12, x21, x22 + [t + 1.0, x11 + 2.0, x21 + 3.0, x12 + 4.0, x22 + 5.0] in NormNuclearCone(2, 2) + """, + """ + variables: t, x11, x12, x21, x22, u1, u2, u3, v1, v2, v3 + t + -0.5 * u1 + -0.5 * u3 + -0.5 * v1 + -0.5 * v3 >= -1.0 + [u1, u2, u3, x11 + 2.0, x12 + 4.0, v1, x21 + 3.0, x22 + 5.0, v2, v3] in PositiveSemidefiniteConeTriangle(4) + """, + ) + return +end + end # module TestConstraintNormSpectral.runtests() diff --git a/test/Bridges/Constraint/test_RelativeEntropyBridge.jl b/test/Bridges/Constraint/test_RelativeEntropyBridge.jl index 05cc0d7342..1d10aeb269 100644 --- a/test/Bridges/Constraint/test_RelativeEntropyBridge.jl +++ b/test/Bridges/Constraint/test_RelativeEntropyBridge.jl @@ -182,6 +182,23 @@ function test_runtests() return end +function test_runtests_constant() + MOI.Bridges.runtests( + MOI.Bridges.Constraint.RelativeEntropyBridge, + """ + variables: u, v1, v2, w1, w2 + [u + 1.0, v1 + 2.0, v2 + 3.0, w1 + 4.0, w2 + 5.0] in RelativeEntropyCone(5) + """, + """ + variables: u, v1, v2, w1, w2, y1, y2 + u + -1.0 * y1 + -1.0 * y2 >= -1.0 + [-1.0 * y1, w1 + 4.0, v1 + 2.0] in ExponentialCone() + [-1.0 * y2, w2 + 5.0, v2 + 3.0] in ExponentialCone() + """, + ) + return +end + end # module TestConstraintRelativeEntropyToExponential.runtests() diff --git a/test/Bridges/Constraint/test_SquareBridge.jl b/test/Bridges/Constraint/test_SquareBridge.jl index e9e1c0036a..56715c3798 100644 --- a/test/Bridges/Constraint/test_SquareBridge.jl +++ b/test/Bridges/Constraint/test_SquareBridge.jl @@ -314,6 +314,40 @@ function test_VectorNonlinearFunction_mixed_type() return end +function test_square_bridge_with_constant() + MOI.Bridges.runtests( + MOI.Bridges.Constraint.SquareBridge, + """ + variables: x11, x21, x12, x22 + [x11 + 1.0, x21 + 2.0, x12 + 3.0, x22 + 4.0] in PositiveSemidefiniteConeSquare(2) + """, + """ + variables: x11, x21, x12, x22 + [x11 + 1.0, x12 + 3.0, x22 + 4.0] in PositiveSemidefiniteConeTriangle(2) + x12 + -1.0 * x21 == -1.0 + """, + ) + return +end + +function test_constraint_primal_start() + inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Constraint.Square{Float64}(inner) + x = MOI.add_variables(model, 4) + f = MOI.Utilities.vectorize(1.0 .* x .+ (1.0:4.0)) + ci = MOI.add_constraint(model, f, MOI.PositiveSemidefiniteConeSquare(2)) + bridge = MOI.Bridges.bridge(model, ci) + start = [11.0, 21.0, 12.0, 22.0] + MOI.set(model, MOI.ConstraintPrimalStart(), ci, start) + @test isapprox( + MOI.get(inner, MOI.ConstraintPrimalStart(), bridge.triangle), + [11.0, 12.0, 22.0], + ) + @test ≈(MOI.get(inner, MOI.ConstraintPrimalStart(), bridge.sym[1][2]), -9.0) + @test ≈(MOI.get(model, MOI.ConstraintFunction(), ci), f) + return +end + end # module TestConstraintSquare.runtests()