Skip to content

Commit 1aa993f

Browse files
authored
Merge pull request #446 from matbesancon/numcut-attribute
num cuts as attribute
2 parents 2c008ee + 9e820d9 commit 1aa993f

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

src/MOI_wrapper.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,17 @@ end
153153
function MOI.get(opt::Optimizer, ::MOI.NodeCount)
154154
return MOI.get(opt.oa_model, MOI.NodeCount())
155155
end
156+
157+
"""
158+
NumberOfCuts
159+
160+
Number of cuts added to the outer approximation model.
161+
"""
162+
struct NumberOfCuts <: MOI.AbstractModelAttribute end
163+
164+
MOI.attribute_value_type(::NumberOfCuts) = Int64
165+
MOI.is_set_by_optimize(::NumberOfCuts) = true
166+
167+
function MOI.get(opt::Optimizer, ::NumberOfCuts)
168+
return opt.num_cuts
169+
end

test/JuMP_tests.jl

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ function run_jump_tests(
6262
_psd2,
6363
_expdesign,
6464
_specialorderedset,
65+
_soc1_ncuts,
6566
]
6667
@testset "$inst" for inst in insts
6768
println(inst)
@@ -481,4 +482,56 @@ function _specialorderedset(opt)
481482
return
482483
end
483484

485+
function _soc1_ncuts(opt)
486+
TOL = 1e-4
487+
m = JuMP.Model(opt)
488+
489+
JuMP.@variable(m, x)
490+
JuMP.@objective(m, Min, -x)
491+
xlb1 = JuMP.@constraint(m, x >= 4)
492+
soc1 = JuMP.@constraint(m, [3.5, x] in JuMP.SecondOrderCone())
493+
JuMP.optimize!(m)
494+
@test JuMP.termination_status(m) == MOI.INFEASIBLE
495+
@test JuMP.primal_status(m) == MOI.NO_SOLUTION
496+
497+
JuMP.delete(m, xlb1)
498+
JuMP.optimize!(m)
499+
@test JuMP.termination_status(m) == MOI.OPTIMAL
500+
@test JuMP.primal_status(m) == MOI.FEASIBLE_POINT
501+
@test isapprox(JuMP.objective_value(m), -3.5, atol = TOL)
502+
@test isapprox(JuMP.objective_bound(m), -3.5, atol = TOL)
503+
@test isapprox(JuMP.value(x), 3.5, atol = TOL)
504+
505+
xlb2 = JuMP.@constraint(m, x >= 3.1)
506+
JuMP.set_integer(x)
507+
JuMP.optimize!(m)
508+
@test JuMP.termination_status(m) == MOI.INFEASIBLE
509+
510+
JuMP.delete(m, xlb2)
511+
JuMP.@constraint(m, x >= 0.5)
512+
JuMP.optimize!(m)
513+
@test JuMP.termination_status(m) == MOI.OPTIMAL
514+
@test MOI.get(m, Pajarito.NumberOfCuts()) > 0
515+
@test isapprox(JuMP.objective_value(m), -3, atol = TOL)
516+
@test isapprox(JuMP.objective_bound(m), -3, atol = TOL)
517+
@test isapprox(JuMP.value(x), 3, atol = TOL)
518+
519+
JuMP.@objective(m, Max, -3x)
520+
JuMP.optimize!(m)
521+
@test MOI.get(m, Pajarito.NumberOfCuts()) > 0
522+
@test JuMP.termination_status(m) == MOI.OPTIMAL
523+
@test isapprox(JuMP.objective_value(m), -3, atol = TOL)
524+
@test isapprox(JuMP.objective_bound(m), -3, atol = TOL)
525+
@test isapprox(JuMP.value(x), 1, atol = TOL)
526+
527+
JuMP.set_start_value(x, 1)
528+
JuMP.optimize!(m)
529+
@test JuMP.termination_status(m) == MOI.OPTIMAL
530+
@test MOI.get(m, Pajarito.NumberOfCuts()) > 0
531+
@test isapprox(JuMP.objective_value(m), -3, atol = TOL)
532+
@test isapprox(JuMP.objective_bound(m), -3, atol = TOL)
533+
@test isapprox(JuMP.value(x), 1, atol = TOL)
534+
return
535+
end
536+
484537
end

0 commit comments

Comments
 (0)