Skip to content

Commit 629d6a9

Browse files
committed
Add support for modifying MOI.ConstraintSet in MOI wrapper
1 parent f9ed941 commit 629d6a9

File tree

2 files changed

+172
-47
lines changed

2 files changed

+172
-47
lines changed

ext/KNITROMathOptInterfaceExt.jl

Lines changed: 99 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -498,14 +498,23 @@ function MOI.add_constraint(
498498
if _is_fixed(model, x)
499499
error("Variable $x is fixed. Cannot also set upper bound.")
500500
end
501-
ub = _clamp_inf(set.upper)
502501
model.variable_info[x.value].has_upper_bound = true
503-
KNITRO.@_checked KNITRO.KN_set_var_upbnd(model, _c_column(x), ub)
504502
ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(x.value)
505503
model.constraint_mapping[ci] = convert(Cint, x.value)
504+
MOI.set(model, MOI.ConstraintSet(), ci, set)
506505
return ci
507506
end
508507

508+
function MOI.set(
509+
model::Optimizer,
510+
::MOI.ConstraintSet,
511+
ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}},
512+
s::MOI.LessThan{Float64},
513+
)
514+
KNITRO.@_checked KNITRO.KN_set_var_upbnd(model, ci.value - 1, _clamp_inf(s.upper))
515+
return
516+
end
517+
509518
###
510519
### MOI.VariableIndex -in- GreaterThan
511520
###
@@ -534,14 +543,23 @@ function MOI.add_constraint(
534543
if _is_fixed(model, x)
535544
error("Variable $x is fixed. Cannot also set lower bound.")
536545
end
537-
lb = _clamp_inf(set.lower)
538546
model.variable_info[x.value].has_lower_bound = true
539-
KNITRO.@_checked KNITRO.KN_set_var_lobnd(model, _c_column(x), lb)
540547
ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(x.value)
541548
model.constraint_mapping[ci] = convert(Cint, x.value)
549+
MOI.set(model, MOI.ConstraintSet(), ci, set)
542550
return ci
543551
end
544552

553+
function MOI.set(
554+
model::Optimizer,
555+
::MOI.ConstraintSet,
556+
ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}},
557+
s::MOI.GreaterThan{Float64},
558+
)
559+
KNITRO.@_checked KNITRO.KN_set_var_lobnd(model, ci.value - 1, _clamp_inf(s.lower))
560+
return
561+
end
562+
545563
###
546564
### MOI.VariableIndex -in- Interval
547565
###
@@ -570,17 +588,25 @@ function MOI.add_constraint(
570588
if _is_fixed(model, x)
571589
error("Variable $x is fixed. Cannot also set lower bound.")
572590
end
573-
lb = _clamp_inf(set.lower)
574-
ub = _clamp_inf(set.upper)
575591
model.variable_info[x.value].has_lower_bound = true
576592
model.variable_info[x.value].has_upper_bound = true
577-
KNITRO.@_checked KNITRO.KN_set_var_lobnd(model, _c_column(x), lb)
578-
KNITRO.@_checked KNITRO.KN_set_var_upbnd(model, _c_column(x), ub)
579593
ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{Float64}}(x.value)
580594
model.constraint_mapping[ci] = convert(Cint, x.value)
595+
MOI.set(model, MOI.ConstraintSet(), ci, set)
581596
return ci
582597
end
583598

599+
function MOI.set(
600+
model::Optimizer,
601+
::MOI.ConstraintSet,
602+
ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{Float64}},
603+
s::MOI.Interval{Float64},
604+
)
605+
KNITRO.@_checked KNITRO.KN_set_var_lobnd(model, ci.value - 1, _clamp_inf(s.lower))
606+
KNITRO.@_checked KNITRO.KN_set_var_upbnd(model, ci.value - 1, _clamp_inf(s.upper))
607+
return
608+
end
609+
584610
###
585611
### MOI.VariableIndex -in- EqualTo
586612
###
@@ -612,14 +638,23 @@ function MOI.add_constraint(
612638
if _is_fixed(model, x)
613639
error("Variable $x is already fixed.")
614640
end
615-
eqv = _clamp_inf(set.value)
616641
model.variable_info[x.value].is_fixed = true
617-
KNITRO.@_checked KNITRO.KN_set_var_fxbnd(model, _c_column(x), eqv)
618642
ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{Float64}}(x.value)
619643
model.constraint_mapping[ci] = convert(Cint, x.value)
644+
MOI.set(model, MOI.ConstraintSet(), ci, set)
620645
return ci
621646
end
622647

648+
function MOI.set(
649+
model::Optimizer,
650+
::MOI.ConstraintSet,
651+
ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{Float64}},
652+
s::MOI.EqualTo{Float64},
653+
)
654+
KNITRO.@_checked KNITRO.KN_set_var_fxbnd(model, ci.value - 1, _clamp_inf(s.value))
655+
return
656+
end
657+
623658
###
624659
### ConstraintDualStart :: VariableIndex -in- {LessThan,GreaterThan,EqualTo,Interval}
625660
###
@@ -722,32 +757,62 @@ function MOI.add_constraint(
722757
p = Ref{Cint}(0)
723758
KNITRO.@_checked KNITRO.KN_add_con(model, p)
724759
num_cons = p[]
725-
# Add bound to constraint.
726-
if isa(set, MOI.LessThan{Float64})
727-
val = _clamp_inf(set.upper)
728-
KNITRO.@_checked KNITRO.KN_set_con_upbnd(model, num_cons, val)
729-
elseif isa(set, MOI.GreaterThan{Float64})
730-
val = _clamp_inf(set.lower)
731-
KNITRO.@_checked KNITRO.KN_set_con_lobnd(model, num_cons, val)
732-
elseif isa(set, MOI.EqualTo{Float64})
733-
val = _clamp_inf(set.value)
734-
KNITRO.@_checked KNITRO.KN_set_con_eqbnd(model, num_cons, val)
735-
else
736-
@assert set isa MOI.Interval{Float64}
737-
# Add upper bound.
738-
lb, ub = _clamp_inf(set.lower), _clamp_inf(set.upper)
739-
KNITRO.@_checked KNITRO.KN_set_con_lobnd(model, num_cons, lb)
740-
KNITRO.@_checked KNITRO.KN_set_con_upbnd(model, num_cons, ub)
741-
end
760+
ci = MOI.ConstraintIndex{F,S}(num_cons)
761+
model.constraint_mapping[ci] = num_cons
762+
MOI.set(model, MOI.ConstraintSet(), ci, set)
742763
nnz, columns, coefficients = _canonical_linear_reduction(func)
743764
KNITRO.@_checked(
744765
KNITRO.KN_add_con_linear_struct_one(model, nnz, num_cons, columns, coefficients),
745766
)
746-
ci = MOI.ConstraintIndex{F,S}(num_cons)
747-
model.constraint_mapping[ci] = num_cons
748767
return ci
749768
end
750769

770+
function MOI.set(
771+
model::Optimizer,
772+
::MOI.ConstraintSet,
773+
ci::MOI.ConstraintIndex{F,MOI.GreaterThan{Float64}},
774+
s::MOI.GreaterThan{Float64},
775+
) where {F<:Union{MOI.ScalarAffineFunction{Float64},MOI.ScalarQuadraticFunction{Float64}}}
776+
num_cons = model.constraint_mapping[ci]
777+
KNITRO.@_checked KNITRO.KN_set_con_lobnd(model, num_cons, _clamp_inf(s.lower))
778+
return
779+
end
780+
781+
function MOI.set(
782+
model::Optimizer,
783+
::MOI.ConstraintSet,
784+
ci::MOI.ConstraintIndex{F,MOI.LessThan{Float64}},
785+
s::MOI.LessThan{Float64},
786+
) where {F<:Union{MOI.ScalarAffineFunction{Float64},MOI.ScalarQuadraticFunction{Float64}}}
787+
num_cons = model.constraint_mapping[ci]
788+
KNITRO.@_checked KNITRO.KN_set_con_upbnd(model, num_cons, _clamp_inf(s.upper))
789+
return
790+
end
791+
792+
function MOI.set(
793+
model::Optimizer,
794+
::MOI.ConstraintSet,
795+
ci::MOI.ConstraintIndex{F,MOI.Interval{Float64}},
796+
s::MOI.Interval{Float64},
797+
) where {F<:Union{MOI.ScalarAffineFunction{Float64},MOI.ScalarQuadraticFunction{Float64}}}
798+
num_cons = model.constraint_mapping[ci]
799+
lb, ub = _clamp_inf(s.lower), _clamp_inf(s.upper)
800+
KNITRO.@_checked KNITRO.KN_set_con_lobnd(model, num_cons, lb)
801+
KNITRO.@_checked KNITRO.KN_set_con_upbnd(model, num_cons, ub)
802+
return
803+
end
804+
805+
function MOI.set(
806+
model::Optimizer,
807+
::MOI.ConstraintSet,
808+
ci::MOI.ConstraintIndex{F,MOI.EqualTo{Float64}},
809+
s::MOI.EqualTo{Float64},
810+
) where {F<:Union{MOI.ScalarAffineFunction{Float64},MOI.ScalarQuadraticFunction{Float64}}}
811+
num_cons = model.constraint_mapping[ci]
812+
KNITRO.@_checked KNITRO.KN_set_con_eqbnd(model, num_cons, _clamp_inf(s.value))
813+
return
814+
end
815+
751816
function MOI.supports(
752817
::Optimizer,
753818
::MOI.ConstraintDualStart,
@@ -807,30 +872,15 @@ function MOI.add_constraint(
807872
p = Ref{Cint}(0)
808873
KNITRO.@_checked KNITRO.KN_add_con(model, p)
809874
num_cons = p[]
810-
# Add upper bound.
811-
if isa(set, MOI.LessThan{Float64})
812-
val = _clamp_inf(set.upper)
813-
KNITRO.@_checked KNITRO.KN_set_con_upbnd(model, num_cons, val)
814-
elseif isa(set, MOI.GreaterThan{Float64})
815-
val = _clamp_inf(set.lower)
816-
KNITRO.@_checked KNITRO.KN_set_con_lobnd(model, num_cons, val)
817-
elseif isa(set, MOI.EqualTo{Float64})
818-
val = _clamp_inf(set.value)
819-
KNITRO.@_checked KNITRO.KN_set_con_eqbnd(model, num_cons, val)
820-
elseif isa(set, MOI.Interval{Float64})
821-
lb = _clamp_inf(set.lower)
822-
ub = _clamp_inf(set.upper)
823-
KNITRO.@_checked KNITRO.KN_set_con_lobnd(model, num_cons, lb)
824-
KNITRO.@_checked KNITRO.KN_set_con_upbnd(model, num_cons, ub)
825-
end
875+
ci = MOI.ConstraintIndex{typeof(func),typeof(set)}(num_cons)
876+
model.constraint_mapping[ci] = num_cons
877+
MOI.set(model, MOI.ConstraintSet(), ci, set)
826878
nnz, columns, coefficients = _canonical_linear_reduction(func)
827879
KNITRO.@_checked(
828880
KNITRO.KN_add_con_linear_struct_one(model, nnz, num_cons, columns, coefficients),
829881
)
830882
nnz, I, J, V = _canonical_quadratic_reduction(func)
831883
KNITRO.@_checked KNITRO.KN_add_con_quadratic_struct_one(model, nnz, num_cons, I, J, V)
832-
ci = MOI.ConstraintIndex{typeof(func),typeof(set)}(num_cons)
833-
model.constraint_mapping[ci] = num_cons
834884
return ci
835885
end
836886

@@ -1397,6 +1447,8 @@ function MOI.optimize!(model::Optimizer)
13971447
end
13981448
model.nlp_loaded = true
13991449
end
1450+
empty!(model.x)
1451+
empty!(model.lambda)
14001452
KNITRO.KN_solve(model.inner)
14011453
model.number_solved += 1
14021454
return

test/MOI_wrapper.jl

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,79 @@ function test_issue_370()
421421
return
422422
end
423423

424+
function test_issue_377_variable_index()
425+
for ((set_1, value_1), (set_2, value_2), sense) in [
426+
(MOI.GreaterThan(1.0) => 1.0, MOI.GreaterThan(2.0) => 2.0, MOI.MIN_SENSE),
427+
(MOI.LessThan(1.0) => 1.0, MOI.LessThan(2.0) => 2.0, MOI.MAX_SENSE),
428+
(MOI.EqualTo(1.0) => 1.0, MOI.EqualTo(2.0) => 2.0, MOI.MAX_SENSE),
429+
(MOI.Interval(1.0, 2.0) => 1.0, MOI.Interval(2.0, 3.0) => 2.0, MOI.MIN_SENSE),
430+
(MOI.Interval(1.0, 2.0) => 2.0, MOI.Interval(2.0, 3.0) => 3.0, MOI.MAX_SENSE),
431+
]
432+
model = KNITRO.Optimizer(; license_manager=LICENSE_MANAGER)
433+
MOI.set(model, MOI.Silent(), true)
434+
x = MOI.add_variable(model)
435+
MOI.set(model, MOI.ObjectiveSense(), sense)
436+
f = 1.0 * x
437+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
438+
c = MOI.add_constraint(model, x, set_1)
439+
MOI.optimize!(model)
440+
@test isapprox(MOI.get(model, MOI.VariablePrimal(), x), value_1; atol=1e-6)
441+
MOI.set(model, MOI.ConstraintSet(), c, set_2)
442+
MOI.optimize!(model)
443+
@test isapprox(MOI.get(model, MOI.VariablePrimal(), x), value_2; atol=1e-6)
444+
end
445+
return
446+
end
447+
448+
function test_issue_377_scalar_affine()
449+
for ((set_1, value_1), (set_2, value_2), sense) in [
450+
(MOI.GreaterThan(1.0) => 1.0, MOI.GreaterThan(2.0) => 2.0, MOI.MIN_SENSE),
451+
(MOI.LessThan(1.0) => 1.0, MOI.LessThan(2.0) => 2.0, MOI.MAX_SENSE),
452+
(MOI.EqualTo(1.0) => 1.0, MOI.EqualTo(2.0) => 2.0, MOI.MAX_SENSE),
453+
(MOI.Interval(1.0, 2.0) => 1.0, MOI.Interval(2.0, 3.0) => 2.0, MOI.MIN_SENSE),
454+
(MOI.Interval(1.0, 2.0) => 2.0, MOI.Interval(2.0, 3.0) => 3.0, MOI.MAX_SENSE),
455+
]
456+
model = KNITRO.Optimizer(; license_manager=LICENSE_MANAGER)
457+
MOI.set(model, MOI.Silent(), true)
458+
x = MOI.add_variable(model)
459+
MOI.set(model, MOI.ObjectiveSense(), sense)
460+
f = 1.0 * x
461+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
462+
c = MOI.add_constraint(model, 1.0 * x, set_1)
463+
MOI.optimize!(model)
464+
@test isapprox(MOI.get(model, MOI.VariablePrimal(), x), value_1; atol=1e-6)
465+
MOI.set(model, MOI.ConstraintSet(), c, set_2)
466+
MOI.optimize!(model)
467+
@test isapprox(MOI.get(model, MOI.VariablePrimal(), x), value_2; atol=1e-6)
468+
end
469+
return
470+
end
471+
472+
function test_issue_377_scalar_quadratic()
473+
for ((set_1, value_1), (set_2, value_2), sense) in [
474+
(MOI.GreaterThan(1.0) => 1.0, MOI.GreaterThan(2.0) => 2.0, MOI.MIN_SENSE),
475+
(MOI.LessThan(1.0) => 1.0, MOI.LessThan(2.0) => 2.0, MOI.MAX_SENSE),
476+
(MOI.EqualTo(1.0) => 1.0, MOI.EqualTo(2.0) => 2.0, MOI.MAX_SENSE),
477+
(MOI.Interval(1.0, 2.0) => 1.0, MOI.Interval(2.0, 3.0) => 2.0, MOI.MIN_SENSE),
478+
(MOI.Interval(1.0, 2.0) => 2.0, MOI.Interval(2.0, 3.0) => 3.0, MOI.MAX_SENSE),
479+
]
480+
model = KNITRO.Optimizer(; license_manager=LICENSE_MANAGER)
481+
MOI.set(model, MOI.Silent(), true)
482+
x = MOI.add_variable(model)
483+
MOI.add_constraint(model, x, MOI.GreaterThan(0.0))
484+
MOI.set(model, MOI.ObjectiveSense(), sense)
485+
f = 1.0 * x
486+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
487+
c = MOI.add_constraint(model, 1.0 * x * x, set_1)
488+
MOI.optimize!(model)
489+
@test isapprox(MOI.get(model, MOI.VariablePrimal(), x), sqrt(value_1); atol=1e-6)
490+
MOI.set(model, MOI.ConstraintSet(), c, set_2)
491+
MOI.optimize!(model)
492+
@test isapprox(MOI.get(model, MOI.VariablePrimal(), x), sqrt(value_2); atol=1e-6)
493+
end
494+
return
495+
end
496+
424497
end
425498

426499
TestMOIWrapper.runtests()

0 commit comments

Comments
 (0)