Skip to content

Commit 58c316e

Browse files
authored
Convert quadratic to affine even when there is no parameters (#166)
* Convert quadratic to affine even when there is no parameters * Fix format * Add test * Fixes
1 parent 42d5d6b commit 58c316e

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

src/MOI_wrapper.jl

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,13 +492,22 @@ function MOI.set(
492492
end
493493

494494
function MOI.get(
495-
model::Optimizer,
495+
model::Optimizer{T},
496496
attr::MOI.ConstraintFunction,
497497
ci::MOI.ConstraintIndex,
498-
)
498+
) where {T}
499499
if haskey(model.quadratic_outer_to_inner, ci)
500500
inner_ci = model.quadratic_outer_to_inner[ci]
501-
return _original_function(model.quadratic_constraint_cache[inner_ci])
501+
if haskey(model.quadratic_constraint_cache, inner_ci)
502+
return _original_function(
503+
model.quadratic_constraint_cache[inner_ci],
504+
)
505+
else
506+
return convert(
507+
MOI.ScalarQuadraticFunction{T},
508+
MOI.get(model.optimizer, attr, inner_ci),
509+
)
510+
end
502511
elseif haskey(model.affine_outer_to_inner, ci)
503512
inner_ci = model.affine_outer_to_inner[ci]
504513
return _original_function(model.affine_constraint_cache[inner_ci])
@@ -781,6 +790,31 @@ function MOI.add_constraint(
781790
set::MOI.AbstractScalarSet,
782791
) where {T}
783792
if !_has_parameters(f)
793+
# The user might construct the expression `*(f::Vector{AffExpr}, p)`
794+
# where `p` is a parameter. This results in a `Vector{QuadExpr}`
795+
# and hence in `ScalarQuadraticFunction` constraints.
796+
# If some entries of `f` are zero, then `has_parameters` will be zero for
797+
# the resulting constraint. We should however still turn it into an affine
798+
# function like the other entries.
799+
if _is_affine(f)
800+
fa = MOI.ScalarAffineFunction(f.affine_terms, f.constant)
801+
inner_ci = MOI.Utilities.normalize_and_add_constraint(
802+
model.optimizer,
803+
fa,
804+
set,
805+
)
806+
model.last_quad_add_added += 1
807+
outer_ci =
808+
MOI.ConstraintIndex{MOI.ScalarQuadraticFunction{T},typeof(set)}(
809+
model.last_quad_add_added,
810+
)
811+
model.quadratic_outer_to_inner[outer_ci] = inner_ci
812+
model.constraint_outer_to_inner[outer_ci] = inner_ci
813+
model.quadratic_constraint_cache_set[inner_ci] = set
814+
return outer_ci
815+
else
816+
return _add_constraint_direct_and_cache_map!(model, f, set)
817+
end
784818
return _add_constraint_direct_and_cache_map!(model, f, set)
785819
else
786820
return _add_constraint_with_parameters_on_function(model, f, set)

test/moi_tests.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,3 +1899,23 @@ function test_getters()
18991899
) isa POI.ParametricQuadraticFunction{Float64}
19001900
return
19011901
end
1902+
1903+
function test_no_quadratic_terms()
1904+
optimizer = POI.Optimizer(GLPK.Optimizer())
1905+
MOI.set(optimizer, MOI.Silent(), true)
1906+
x = MOI.add_variable(optimizer)
1907+
func = MOI.Utilities.canonical(1.0 * x * x + 1.0 * x - 1.0 * x * x)
1908+
set = MOI.LessThan(0.0)
1909+
c = MOI.add_constraint(optimizer, func, set)
1910+
@test c isa MOI.ConstraintIndex{typeof(func),typeof(set)}
1911+
@test MOI.get(optimizer, MOI.ConstraintFunction(), c) func
1912+
@test MOI.get(optimizer, MOI.ConstraintSet(), c) == set
1913+
MOI.set(optimizer, MOI.ConstraintName(), c, "name")
1914+
@test MOI.get(optimizer, MOI.ConstraintName(), c) == "name"
1915+
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)
1916+
obj_func = 1.0 * x
1917+
MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func)
1918+
MOI.optimize!(optimizer)
1919+
@test MOI.get(optimizer, MOI.ConstraintDual(), c) -1 atol = ATOL
1920+
return
1921+
end

0 commit comments

Comments
 (0)