Skip to content

Commit

Permalink
Add bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Sep 24, 2023
1 parent 123185d commit 4bf75be
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 22 deletions.
51 changes: 32 additions & 19 deletions src/QCQP/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ end
MOI.is_empty(model::Optimizer) = MOI.is_empty(model.model)
MOI.empty!(model::Optimizer) = MOI.empty!(model.model)

Check warning on line 23 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L22-L23

Added lines #L22 - L23 were not covered by tests

MOI.is_valid(model::Optimizer, i::MOI.Index) = MOI.is_valid(model.model, i)

Check warning on line 25 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L25

Added line #L25 was not covered by tests

function MOI.get(

Check warning on line 27 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L27

Added line #L27 was not covered by tests
model::Optimizer,
attr::MOI.AbstractConstraintAttribute,
ci::MOI.ConstraintIndex,
)
return MOI.get(model.model, attr, ci)

Check warning on line 32 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L32

Added line #L32 was not covered by tests
end

MOI.add_variable(model::Optimizer) = MOI.add_variable(model.model)

Check warning on line 35 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L35

Added line #L35 was not covered by tests

function MOI.supports_add_constrained_variable(

Check warning on line 37 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L37

Added line #L37 was not covered by tests
Expand Down Expand Up @@ -100,31 +110,20 @@ end

MOI.optimize!(model::Optimizer) = MOI.optimize!(model.model)

Check warning on line 111 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L111

Added line #L111 was not covered by tests

function _quad_convert(index, p::MP.AbstractPolynomialLike{T}, div) where {T}
function _quad_convert(p::MP.AbstractPolynomialLike{T}, index, div) where {T}
q = zero(MOI.ScalarQuadraticFunction{T})
for t in MP.terms(p)
α = MP.coefficient(t)
mono = MP.monomial(t)
if MP.degree(mono) == 0
MA.operate!(+, q, α)

Check warning on line 119 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L113-L119

Added lines #L113 - L119 were not covered by tests
else
vars = MP.effective_variables(mono)
if MP.degree(mono) == 1
@assert length(vars) == 1
MA.operate!(MA.add_mul, q, α, index(first(vars)))
elseif MP.degree(mono) == 2
x = first(vars)
if length(vars) == 1
y = x
else
@assert length(vars) == 2
y = vars[2]
end
MA.operate!(MA.add_mul, q, α, index(x), index(y))
if haskey(index, mono)
MA.operate!(MA.add_mul, q, α, index[mono])

Check warning on line 122 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L121-L122

Added lines #L121 - L122 were not covered by tests
else
x = div[mono]
y = MP.div_multiple(mono, x)
MA.operate!(MA.add_mul, q, α, index(x), index(y))
MA.operate!(MA.add_mul, q, α, index[x], index[y])

Check warning on line 126 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L124-L126

Added lines #L124 - L126 were not covered by tests
end
end
end
Expand All @@ -140,11 +139,19 @@ end

function monomial_variable_index(model::Optimizer{T}, d::Dict, div, mono::MP.AbstractMonomialLike) where {T}
if !haskey(d, mono)
d[mono] = MOI.add_variable(model.model)
x = div[mono]
vx = monomial_variable_index(model, d, div, x)
y = MP.div_multiple(mono, x)
vy = monomial_variable_index(model, d, div, y)
lx, ux = MOI.Utilities.get_bounds(model, T, vx)
ly, uy = MOI.Utilities.get_bounds(model, T, vy)
bounds = (lx * ly, lx * uy, ux * ly, ux * uy)
l = min(bounds...)
if vx == vy
l = max(l, zero(T))

Check warning on line 151 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L140-L151

Added lines #L140 - L151 were not covered by tests
end
u = max(bounds...)
d[mono], _ = MOI.add_constrained_variable(model.model, MOI.Interval(l, u))
MOI.add_constraint(model,
MA.@rewrite(one(T) * d[mono] - one(T) * vx * vy),

Check warning on line 156 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L153-L156

Added lines #L153 - L156 were not covered by tests
MOI.EqualTo(zero(T)),
Expand All @@ -159,10 +166,16 @@ function MOI.Utilities.final_touch(model::Optimizer{T}, _) where {T}
p = model.objective.polynomial
monos = MP.monomials(p)
div = decompose(monos)
function index(mono)
return monomial_variable_index(model, d, div, mono)
for mono in sort(collect(keys(div)))
if haskey(d, mono)
continue

Check warning on line 171 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L163-L171

Added lines #L163 - L171 were not covered by tests
end
a = div[mono]
monomial_variable_index(model, d, div, a)
b = MP.div_multiple(mono, a)
monomial_variable_index(model, d, div, b)
end
obj = _quad_convert(index, p, div)
obj = _quad_convert(p, d, div)
MOI.set(model.model, MOI.ObjectiveFunction{typeof(obj)}(), obj)

Check warning on line 179 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L173-L179

Added lines #L173 - L179 were not covered by tests
end
return

Check warning on line 181 in src/QCQP/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/QCQP/MOI_wrapper.jl#L181

Added line #L181 was not covered by tests
Expand Down
2 changes: 1 addition & 1 deletion src/nl_to_polynomial.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ end

function _scalar_polynomial(d::Dict{K,V}, ::Type{T}, poly) where {T,K,V}
variable_map = collect(d)
sort!(variable_map, by = x -> x[2])
sort!(variable_map, by = x -> x[2], rev = true)
variables = [x[1] for x in variable_map]
P = MP.polynomial_type(V, T)
return ScalarPolynomialFunction{T,P}(poly, variables)
Expand Down
17 changes: 15 additions & 2 deletions test/qcqp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,30 @@ function test_objective(x, y, T)
inner = Model{T}()
optimizer = MOI.Utilities.MockOptimizer(inner)
model = PolyJuMP.JuMP.GenericModel{T}(() -> PolyJuMP.QCQP.Optimizer(optimizer))
PolyJuMP.@variable(model, 0 <= a)
PolyJuMP.@variable(model, 0 <= b)
PolyJuMP.@variable(model, 1 <= a <= 2)
PolyJuMP.@variable(model, -5 <= b <= 3)
PolyJuMP.@constraint(model, a + b >= 1)
PolyJuMP.@objective(model, Min, a^3 - a^2 + 2a*b - b^2 + b^3)
PolyJuMP.optimize!(model)
vis = MOI.get(inner, MOI.ListOfVariableIndices())
@test length(vis) == 4
a, b, b2, a2 = vis
@test MOI.Utilities.get_bounds(inner, T, a) == (1, 2)
@test MOI.Utilities.get_bounds(inner, T, b) == (-5, 3)
@test MOI.Utilities.get_bounds(inner, T, a2) == (1, 4)
@test MOI.Utilities.get_bounds(inner, T, b2) == (0, 25)
F = MOI.ScalarQuadraticFunction{T}
S = MOI.EqualTo{T}
cis = MOI.get(inner, MOI.ListOfConstraintIndices{F,S}())
@test length(cis) == 2
o = one(T)
@test MOI.get(inner, MOI.ConstraintFunction(), cis[1]) b2 - o * b * b
@test MOI.get(inner, MOI.ConstraintFunction(), cis[2]) a2 - o * a * a
for ci in cis
@test MOI.get(inner, MOI.ConstraintSet(), ci) == MOI.EqualTo(zero(T))
end
@test MOI.get(inner, MOI.ObjectiveFunction{F}())
-o * a2 + 2o * a * b - o * b2 + o * a * a2 + o * b * b2
end

function runtests(x, y, T)
Expand Down

0 comments on commit 4bf75be

Please sign in to comment.