Skip to content

Commit cb498bc

Browse files
authored
Remove trivial constraints form split_zero and fix index mapping of psd (#3)
1 parent 7a90ad8 commit cb498bc

File tree

3 files changed

+92
-20
lines changed

3 files changed

+92
-20
lines changed

src/Bridges/Constraint/split_zero.jl

+39-13
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,30 @@ end
1414
similar_type(::Type{<:MOI.VectorAffineFunction}, T::Type) = MOI.VectorAffineFunction{T}
1515

1616
struct SplitZeroBridge{T, F<:MOI.Utilities.TypedLike{T}, G<:MOI.Utilities.TypedLike{Complex{T}}} <: MOI.Bridges.Constraint.AbstractBridge
17-
real::MOI.ConstraintIndex{F, MOI.Zeros}
18-
imag::MOI.ConstraintIndex{F, MOI.Zeros}
17+
dimension::Int
18+
constraint::MOI.ConstraintIndex{F, MOI.Zeros}
19+
real_indices::Vector{Int}
20+
imag_indices::Vector{Int}
21+
end
22+
function _nonzero_indices(func::MOI.AbstractVectorFunction)
23+
return [i for (i, scalar_func) in enumerate(MOIU.scalarize(func)) if !iszero(scalar_func)]
1924
end
2025
function MOI.Bridges.Constraint.bridge_constraint(
2126
::Type{SplitZeroBridge{T, F, G}}, model::MOI.ModelLike,
2227
f::G,
2328
set::MOI.Zeros
2429
) where {T, F, G}
25-
real_con = MOI.add_constraint(model, operate_coefficients(real, T, f), set)
26-
imag_con = MOI.add_constraint(model, operate_coefficients(imag, T, f), set)
27-
return SplitZeroBridge{T, F, G}(real_con, imag_con)
30+
real_part = operate_coefficients(real, T, f)
31+
imag_part = operate_coefficients(imag, T, f)
32+
real_indices = _nonzero_indices(real_part)
33+
imag_indices = _nonzero_indices(imag_part)
34+
func = MOIU.operate(
35+
vcat, T,
36+
MOIU.eachscalar(real_part)[real_indices],
37+
MOIU.eachscalar(imag_part)[imag_indices]
38+
)
39+
constraint = MOI.add_constraint(model, func, MOI.Zeros(length(real_indices) + length(imag_indices)))
40+
return SplitZeroBridge{T, F, G}(MOI.dimension(set), constraint, real_indices, imag_indices)
2841
end
2942

3043
function MOI.supports_constraint(
@@ -45,17 +58,16 @@ end
4558
# Attributes, Bridge acting as a model
4659
function MOI.get(::SplitZeroBridge{T, F},
4760
::MOI.NumberOfConstraints{F, MOI.Zeros}) where {T, F}
48-
return 2
61+
return 1
4962
end
5063
function MOI.get(bridge::SplitZeroBridge{T, F},
5164
::MOI.ListOfConstraintIndices{F, MOI.Zeros}) where {T, F}
52-
return [bridge.real, bridge.imag]
65+
return [bridge.constraint]
5366
end
5467

5568
# Indices
5669
function MOI.delete(model::MOI.ModelLike, bridge::SplitZeroBridge)
57-
MOI.delete(model, bridge.imag)
58-
MOI.delete(model, bridge.real)
70+
MOI.delete(model, bridge.constraint)
5971
end
6072

6173
# Attributes, Bridge acting as a constraint
@@ -68,10 +80,24 @@ function MOI.supports(
6880
end
6981
function MOI.get(model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal, MOI.ConstraintPrimalStart, MOI.ConstraintDual, MOI.ConstraintDualStart},
7082
bridge::SplitZeroBridge)
71-
return MOI.get(model, attr, bridge.real) + im * MOI.get(model, attr, bridge.imag)
83+
values = MOI.get(model, attr, bridge.constraint)
84+
output = zeros(Complex{eltype(values)}, bridge.dimension)
85+
for (i, idx) in enumerate(bridge.real_indices)
86+
output[idx] = values[i]
87+
end
88+
for (i, idx) in enumerate(bridge.imag_indices)
89+
output[idx] = values[length(bridge.real_indices) + i] * im
90+
end
91+
return output
7292
end
7393
function MOI.set(model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimalStart, MOI.ConstraintDualStart},
74-
bridge::SplitZeroBridge, value)
75-
MOI.set(model, attr, bridge.real, map(real, value))
76-
MOI.set(model, attr, bridge.imag, map(imag, value))
94+
bridge::SplitZeroBridge{T}, value) where T
95+
input = Vector{T}(undef, length(bridge.real_indices) + length(bridge.imag_indices))
96+
for (i, idx) in enumerate(bridge.real_indices)
97+
input[i] = real(value[idx])
98+
end
99+
for (i, idx) in enumerate(bridge.imag_indices)
100+
input[length(bridge.real_indices) + i] = imag(value[idx])
101+
end
102+
MOI.set(model, attr, bridge.constraint, input)
77103
end

src/Bridges/Variable/psd.jl

+7-5
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,30 @@ function MOIB.Variable.bridge_constrained_variable(
5656
k22 = MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(n))
5757
X11() = MOI.SingleVariable(variables[k11])
5858
X12() = MOI.SingleVariable(variables[k12])
59-
X21() = MOI.SingleVariable(variables[k21])
59+
function X21(i, j)
60+
I = j
61+
J = n + i
62+
k21 = MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(J - 1)) + I
63+
return MOI.SingleVariable(variables[k21])
64+
end
6065
X22() = MOI.SingleVariable(variables[k22])
6166
con_11_22 = EQ{T}[]
6267
con12diag = EQ{T}[]
6368
con_12_21 = EQ{T}[]
6469
for j in 1:n
65-
k21 -= n + 1 - j
6670
k22 += n
6771
for i in 1:j
6872
k11 += 1
6973
k12 += 1
70-
k21 -= 1
7174
k22 += 1
7275
push!(con_11_22, MOI.add_constraint(model, MOI.Utilities.operate(-, T, X11(), X22()), MOI.EqualTo(zero(T))))
7376
if i == j
7477
push!(con12diag, MOI.add_constraint(model, convert(MOI.ScalarAffineFunction{T}, X12()), MOI.EqualTo(zero(T))))
7578
else
76-
push!(con_12_21, MOI.add_constraint(model, MOI.Utilities.operate(+, T, X21(), X12()), MOI.EqualTo(zero(T))))
79+
push!(con_12_21, MOI.add_constraint(model, MOI.Utilities.operate(+, T, X21(i, j), X12()), MOI.EqualTo(zero(T))))
7780
end
7881
end
7982
k12 += n
80-
k21 -= n - j
8183
end
8284

8385
return HermitianToSymmetricPSDBridge(variables, psd_constraint, con_11_22, con12diag, con_12_21)

test/runtests.jl

+46-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,23 @@ function projection_test(optimizer, config)
4949
@test MOI.get(optimizer, MOI.ConstraintDual(), cx) dual atol=atol rtol=rtol
5050
end
5151

52-
function zero_test(optimizer, config)
52+
function hermitian_psd_test(optimizer, config)
53+
atol = config.atol
54+
rtol = config.rtol
55+
56+
MOI.empty!(optimizer)
57+
set = COI.HermitianPositiveSemidefiniteConeTriangle(3)
58+
x, cx = MOI.add_constrained_variables(optimizer, set)
59+
fx = MOI.SingleVariable.(x)
60+
MOI.add_constraints(optimizer, fx, MOI.EqualTo.([1.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0]))
61+
MOI.optimize!(optimizer)
62+
primal = [1.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0]
63+
@test MOI.get(optimizer, MOI.VariablePrimal(), x) primal atol=atol rtol=rtol
64+
@test MOI.get(optimizer, MOI.ConstraintPrimal(), cx) primal atol=atol rtol=rtol
65+
@test MOI.get(optimizer, MOI.ConstraintDual(), cx) zeros(9) atol=atol rtol=rtol
66+
end
67+
68+
function zero_1_test(optimizer, config)
5369
atol = config.atol
5470
rtol = config.rtol
5571

@@ -71,14 +87,42 @@ function zero_test(optimizer, config)
7187
@test MOI.get(optimizer, MOI.ConstraintDual(), c) [0.0 + 0.0im] atol=atol rtol=rtol
7288
end
7389

90+
function zero_2_test(optimizer, config)
91+
atol = config.atol
92+
rtol = config.rtol
93+
94+
MOI.empty!(optimizer)
95+
x, cx = MOI.add_constrained_variables(optimizer, MOI.Nonnegatives(1))
96+
fx = MOI.SingleVariable.(x)
97+
func = (1.0 + 0.0im) * fx[1] + 1.0im * fx[1] - 2.0im - (1.0 + 0.0im) * fx[1]
98+
c = MOI.add_constraint(
99+
optimizer,
100+
MOI.Utilities.operate(vcat, Complex{Float64}, func),
101+
MOI.Zeros(1)
102+
)
103+
MOI.optimize!(optimizer)
104+
@test MOI.get(optimizer, MOI.TerminationStatus()) == MOI.OPTIMAL
105+
@test MOI.get(optimizer, MOI.VariablePrimal(), x) [2.0] atol=atol rtol=rtol
106+
@test MOI.get(optimizer, MOI.ConstraintPrimal(), cx) [2.0] atol=atol rtol=rtol
107+
@test MOI.get(optimizer, MOI.ConstraintDual(), cx) zeros(1) atol=atol rtol=rtol
108+
@test MOI.get(optimizer, MOI.ConstraintPrimal(), c) [0.0 + 0.0im] atol=atol rtol=rtol
109+
@test MOI.get(optimizer, MOI.ConstraintDual(), c) [0.0 + 0.0im] atol=atol rtol=rtol
110+
end
111+
74112
import CSDP
75113
@testset "CSDP" begin
76114
config = MOI.Test.TestConfig(atol=1e-4, rtol=1e-4)
77115
bridged = MOI.instantiate(CSDP.Optimizer, with_bridge_type=Float64)
78116
MOI.Bridges.add_bridge(bridged, COI.Bridges.Variable.HermitianToSymmetricPSDBridge{Float64})
79117
projection_test(bridged, config)
118+
hermitian_psd_test(bridged, config)
80119
bridged = MOI.Bridges.LazyBridgeOptimizer(MOI.Utilities.CachingOptimizer(MOI.Utilities.Model{Float64}(), CSDP.Optimizer()))
81120
MOI.Bridges.add_bridge(bridged, MOI.Bridges.Constraint.ScalarizeBridge{Float64})
82121
MOI.Bridges.add_bridge(bridged, COI.Bridges.Constraint.SplitZeroBridge{Float64})
83-
zero_test(bridged, config)
122+
zero_1_test(bridged, config)
123+
cis = MOI.get(bridged.model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}}())
124+
@test length(cis) == 2
125+
zero_2_test(bridged, config)
126+
cis = MOI.get(bridged.model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}}())
127+
@test length(cis) == 1
84128
end

0 commit comments

Comments
 (0)