From 110f88564b06a43c95f9afb81c6d0c74b25c9c05 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 22 Jul 2021 11:50:55 +1200 Subject: [PATCH 1/5] Refactor .objective field of Utilities.Model into a struct Currently needs test_broken due to the isapprox issue #1484 --- src/Utilities/Utilities.jl | 1 + src/Utilities/model.jl | 112 ++++------ src/Utilities/objective_function_container.jl | 208 ++++++++++++++++++ src/Utilities/vector_of_constraints.jl | 3 + .../Utilities/objective_function_container.jl | 132 +++++++++++ 5 files changed, 382 insertions(+), 74 deletions(-) create mode 100644 src/Utilities/objective_function_container.jl create mode 100644 test/Utilities/objective_function_container.jl diff --git a/src/Utilities/Utilities.jl b/src/Utilities/Utilities.jl index 6782ae72e5..e7e74b241f 100644 --- a/src/Utilities/Utilities.jl +++ b/src/Utilities/Utilities.jl @@ -59,6 +59,7 @@ include("copy.jl") include("results.jl") include("variables.jl") +include("objective_function_container.jl") include("vector_bounds.jl") include("vector_of_constraints.jl") include("struct_of_constraints.jl") diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index bb7d389217..9a2cbc068d 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -110,16 +110,14 @@ function _delete_variable( MOI.ConstraintIndex{MOI.SingleVariable,MOI.Semiinteger{T}}(vi.value), ) end -_fast_in(vi1::MOI.VariableIndex, vi2::MOI.VariableIndex) = vi1 == vi2 -_fast_in(vi::MOI.VariableIndex, vis::Set{MOI.VariableIndex}) = vi in vis + function MOI.delete(model::AbstractModel, vi::MOI.VariableIndex) - vis = [vi] - _throw_if_cannot_delete(model.constraints, vis, vi) + _throw_if_cannot_delete(model.constraints, [vi], vi) _delete_variable(model, vi) _deleted_constraints(model.constraints, vi) do ci return delete!(model.con_to_name, ci) end - model.objective = remove_variable(model.objective, vi) + MOI.delete(model.objective, vi) model.name_to_con = nothing return end @@ -135,8 +133,7 @@ function MOI.delete(model::AbstractModel, vis::Vector{MOI.VariableIndex}) for vi in vis _delete_variable(model, vi) end - keep = x -> MOI.is_valid(model, x) - model.objective = filter_variables(keep, model.objective) + MOI.delete(model.objective, vis) model.name_to_con = nothing return end @@ -304,82 +301,65 @@ function MOI.get( end # Objective -MOI.get(model::AbstractModel, ::MOI.ObjectiveSense) = model.sense -MOI.supports(model::AbstractModel, ::MOI.ObjectiveSense) = true + +function MOI.get( + model::AbstractModel, + attr::Union{ + MOI.ObjectiveSense, + MOI.ObjectiveFunction, + MOI.ObjectiveFunctionType, + }, +) + return MOI.get(model.objective, attr) +end + +function MOI.supports( + model::AbstractModel, + attr::Union{MOI.ObjectiveSense,MOI.ObjectiveFunction}, +) + return MOI.supports(model.objective, attr) +end + function MOI.set( - model::AbstractModel{T}, + model::AbstractModel, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense, -) where {T} - if sense == MOI.FEASIBILITY_SENSE - model.objectiveset = false - model.objective = zero(MOI.ScalarAffineFunction{T}) - end - model.senseset = true - model.sense = sense +) + MOI.set(model.objective, attr, sense) return end -function MOI.get(model::AbstractModel, ::MOI.ObjectiveFunctionType) - return MOI.typeof(model.objective) -end -function MOI.get(model::AbstractModel, ::MOI.ObjectiveFunction{T})::T where {T} - return model.objective -end -function MOI.supports( - model::AbstractModel{T}, - ::MOI.ObjectiveFunction{ - <:Union{ - MOI.SingleVariable, - MOI.ScalarAffineFunction{T}, - MOI.ScalarQuadraticFunction{T}, - }, - }, -) where {T} - return true -end function MOI.set( model::AbstractModel, attr::MOI.ObjectiveFunction{F}, f::F, ) where {F<:MOI.AbstractFunction} - if !MOI.supports(model, attr) - throw(MOI.UnsupportedAttribute(attr)) - end - model.objectiveset = true - # f needs to be copied, see #2 - model.objective = copy(f) + MOI.set(model.objective, attr, f) return end function MOI.modify( model::AbstractModel, - ::MOI.ObjectiveFunction, + attr::MOI.ObjectiveFunction, change::MOI.AbstractFunctionModification, ) - model.objective = modify_function(model.objective, change) - model.objectiveset = true + MOI.modify(model.objective, attr, change) return end function MOI.get(::AbstractModel, ::MOI.ListOfOptimizerAttributesSet) return MOI.AbstractOptimizerAttribute[] end + function MOI.get( model::AbstractModel, - ::MOI.ListOfModelAttributesSet, + attr::MOI.ListOfModelAttributesSet, )::Vector{MOI.AbstractModelAttribute} - listattr = MOI.AbstractModelAttribute[] - if model.senseset - push!(listattr, MOI.ObjectiveSense()) - end - if model.objectiveset - push!(listattr, MOI.ObjectiveFunction{typeof(model.objective)}()) - end + ret = MOI.get(model.objective, attr) if !isempty(model.name) - push!(listattr, MOI.Name()) + push!(ret, MOI.Name()) end - return listattr + return ret end # Constraints @@ -557,20 +537,14 @@ end function MOI.is_empty(model::AbstractModel) return isempty(model.name) && - !model.senseset && - !model.objectiveset && - isempty(model.objective.terms) && - iszero(model.objective.constant) && + MOI.is_empty(model.objective) && MOI.is_empty(model.constraints) && MOI.is_empty(model.variable_bounds) end function MOI.empty!(model::AbstractModel{T}) where {T} model.name = "" - model.senseset = false - model.sense = MOI.FEASIBILITY_SENSE - model.objectiveset = false - model.objective = zero(MOI.ScalarAffineFunction{T}) + MOI.empty!(model.objective) MOI.empty!(model.variable_bounds) empty!(model.var_to_name) model.name_to_var = nothing @@ -848,14 +822,7 @@ for (loop_name, loop_super_type) in [ """ mutable struct $name{T,C} <: $super_type{T} name::String - senseset::Bool - sense::MOI.OptimizationSense - objectiveset::Bool - objective::Union{ - MOI.SingleVariable, - MOI.ScalarAffineFunction{T}, - MOI.ScalarQuadraticFunction{T}, - } + objective::ObjectiveFunctionContainer{T} variable_bounds::SingleVariableConstraints{T} constraints::C var_to_name::Dict{MOI.VariableIndex,String} @@ -869,10 +836,7 @@ for (loop_name, loop_super_type) in [ function $name{T,C}() where {T,C} return new{T,C}( EMPTYSTRING, - false, - MOI.FEASIBILITY_SENSE, - false, - zero(MOI.ScalarAffineFunction{T}), + ObjectiveFunctionContainer{T}(), SingleVariableConstraints{T}(), C(), Dict{MOI.VariableIndex,String}(), diff --git a/src/Utilities/objective_function_container.jl b/src/Utilities/objective_function_container.jl new file mode 100644 index 0000000000..3ae0f7559b --- /dev/null +++ b/src/Utilities/objective_function_container.jl @@ -0,0 +1,208 @@ +""" + ObjectiveFunctionContainer{T} + +A helper struct to simplify the handling of objective functions in +Utilities.Model. +""" +mutable struct ObjectiveFunctionContainer{T} + is_sense_set::Bool + sense::MOI.OptimizationSense + is_function_set::Bool + single_variable::Union{Nothing,MOI.SingleVariable} + scalar_affine::Union{Nothing,MOI.ScalarAffineFunction{T}} + scalar_quadratic::Union{Nothing,MOI.ScalarQuadraticFunction{T}} + function ObjectiveFunctionContainer{T}() where {T} + o = new{T}() + MOI.empty!(o) + return o + end +end + +function MOI.empty!(o::ObjectiveFunctionContainer{T}) where {T} + o.is_sense_set = false + o.sense = MOI.FEASIBILITY_SENSE + o.is_function_set = false + o.single_variable = nothing + o.scalar_affine = + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{T}[], zero(T)) + o.scalar_quadratic = nothing + return +end + +function MOI.is_empty(o::ObjectiveFunctionContainer) + return !o.is_sense_set && !o.is_function_set +end + +### +### ObjectiveSense +### + +MOI.supports(::ObjectiveFunctionContainer, ::MOI.ObjectiveSense) = true + +MOI.get(o::ObjectiveFunctionContainer, ::MOI.ObjectiveSense) = o.sense + +function MOI.set(o::ObjectiveFunctionContainer, ::MOI.ObjectiveSense, value) + if value == MOI.FEASIBILITY_SENSE + MOI.empty!(o) + end + o.is_sense_set = true + o.sense = value + return +end + +### +### ObjectiveFunctionType +### + +function MOI.get( + o::ObjectiveFunctionContainer{T}, + ::MOI.ObjectiveFunctionType, +) where {T} + if o.single_variable !== nothing + return MOI.SingleVariable + elseif o.scalar_quadratic !== nothing + return MOI.ScalarQuadraticFunction{T} + end + @assert o.scalar_affine !== nothing + return MOI.ScalarAffineFunction{T} +end + +### +### ObjectiveFunction +### + +function MOI.supports( + ::ObjectiveFunctionContainer{T}, + ::MOI.ObjectiveFunction{ + <:Union{ + MOI.SingleVariable, + MOI.ScalarAffineFunction{T}, + MOI.ScalarQuadraticFunction{T}, + }, + }, +) where {T} + return true +end + +function MOI.get( + o::ObjectiveFunctionContainer{T}, + ::MOI.ObjectiveFunction{F}, +) where {T,F} + if o.single_variable !== nothing + return convert(F, o.single_variable) + elseif o.scalar_quadratic !== nothing + return convert(F, o.scalar_quadratic) + end + @assert o.scalar_affine !== nothing + return convert(F, o.scalar_affine) +end + +function MOI.set( + o::ObjectiveFunctionContainer, + ::MOI.ObjectiveFunction{MOI.SingleVariable}, + f::MOI.SingleVariable, +) + o.is_function_set = true + o.single_variable = copy(f) + o.scalar_affine = nothing + o.scalar_quadratic = nothing + return +end + +function MOI.set( + o::ObjectiveFunctionContainer{T}, + ::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}, + f::MOI.ScalarAffineFunction{T}, +) where {T} + o.is_function_set = true + o.single_variable = nothing + o.scalar_affine = copy(f) + o.scalar_quadratic = nothing + return +end + +function MOI.set( + o::ObjectiveFunctionContainer{T}, + ::MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}, + f::MOI.ScalarQuadraticFunction{T}, +) where {T} + o.is_function_set = true + o.single_variable = nothing + o.scalar_affine = nothing + o.scalar_quadratic = copy(f) + return +end + +### +### MOI.ListOfModelAttributesSet +### + +function MOI.get(o::ObjectiveFunctionContainer, ::MOI.ListOfModelAttributesSet) + ret = MOI.AbstractModelAttribute[] + if o.is_sense_set + push!(ret, MOI.ObjectiveSense()) + end + if o.is_function_set + F = MOI.get(o, MOI.ObjectiveFunctionType()) + push!(ret, MOI.ObjectiveFunction{F}()) + end + return ret +end + +### +### MOI.modify +### + +function MOI.modify( + o::ObjectiveFunctionContainer, + ::MOI.ObjectiveFunction, + change::MOI.AbstractFunctionModification, +) + if o.single_variable !== nothing + o.single_variable = modify_function(o.single_variable, change) + elseif o.scalar_quadratic !== nothing + o.scalar_quadratic = modify_function(o.scalar_quadratic, change) + else + @assert o.scalar_affine !== nothing + o.is_function_set = true + o.scalar_affine = modify_function(o.scalar_affine, change) + end + return +end + +### +### MOI.delete +### + +function MOI.delete(o::ObjectiveFunctionContainer, x::MOI.VariableIndex) + if o.single_variable !== nothing + sense = o.sense + MOI.empty!(o) + if o.is_sense_set + MOI.set(o, MOI.ObjectiveSense(), sense) + end + elseif o.scalar_quadratic !== nothing + o.scalar_quadratic = remove_variable(o.scalar_quadratic, x) + else + @assert o.scalar_affine !== nothing + o.scalar_affine = remove_variable(o.scalar_affine, x) + end + return +end + +function MOI.delete(o::ObjectiveFunctionContainer, x::Vector{MOI.VariableIndex}) + keep = v -> !(v in x) + if o.single_variable !== nothing && o.single_variable.variable in x + sense = o.sense + MOI.empty!(o) + if o.is_sense_set + MOI.set(o, MOI.ObjectiveSense(), sense) + end + elseif o.scalar_quadratic !== nothing + o.scalar_quadratic = filter_variables(keep, o.scalar_quadratic) + else + @assert o.scalar_affine !== nothing + o.scalar_affine = filter_variables(keep, o.scalar_affine) + end + return +end diff --git a/src/Utilities/vector_of_constraints.jl b/src/Utilities/vector_of_constraints.jl index 2af9c7d517..a34731d572 100644 --- a/src/Utilities/vector_of_constraints.jl +++ b/src/Utilities/vector_of_constraints.jl @@ -175,6 +175,9 @@ end # Nothing to do as it's not `VectorOfVariables` constraints _throw_if_cannot_delete(::VectorOfConstraints, vis, fast_in_vis) = nothing +_fast_in(vi1::MOI.VariableIndex, vi2::MOI.VariableIndex) = vi1 == vi2 +_fast_in(vi::MOI.VariableIndex, vis::Set{MOI.VariableIndex}) = vi in vis + function _throw_if_cannot_delete( v::VectorOfConstraints{MOI.VectorOfVariables,S}, vis, diff --git a/test/Utilities/objective_function_container.jl b/test/Utilities/objective_function_container.jl new file mode 100644 index 0000000000..ece292645e --- /dev/null +++ b/test/Utilities/objective_function_container.jl @@ -0,0 +1,132 @@ +module TestObjectiveFunctionContainer + +using Test + +import MathOptInterface + +const MOI = MathOptInterface + +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return +end + +function test_ObjectiveSense() + o = MOI.Utilities.ObjectiveFunctionContainer{Float16}() + @test MOI.get(o, MOI.ListOfModelAttributesSet()) == [] + @test MOI.supports(o, MOI.ObjectiveSense()) + for val in (MOI.MIN_SENSE, MOI.MAX_SENSE, MOI.FEASIBILITY_SENSE) + MOI.set(o, MOI.ObjectiveSense(), val) + @test MOI.get(o, MOI.ObjectiveSense()) == val + end + return +end + +function test_FEASIBILITY_SENSE_clears_objective() + o = MOI.Utilities.ObjectiveFunctionContainer{Float16}() + x = MOI.VariableIndex(1234) + f = MOI.SingleVariable(x) + MOI.set(o, MOI.ObjectiveFunction{MOI.SingleVariable}(), f) + @test MOI.get(o, MOI.ObjectiveFunction{MOI.SingleVariable}()) ≈ f + MOI.set(o, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) + @test !MOI.is_empty(o) + @test o.is_function_set == false + return +end + +function _test_basic_objective(F, T) + o = MOI.Utilities.ObjectiveFunctionContainer{T}() + @test MOI.is_empty(o) + @test MOI.supports(o, MOI.ObjectiveFunction{F}()) + x = MOI.VariableIndex(1234) + f = convert(F, MOI.SingleVariable(x)) + MOI.set(o, MOI.ObjectiveFunction{F}(), f) + @test MOI.get(o, MOI.ObjectiveFunctionType()) == F + @test !MOI.is_empty(o) + @test MOI.get(o, MOI.ListOfModelAttributesSet()) == + Any[MOI.ObjectiveFunction{F}()] + MOI.empty!(o) + @test MOI.is_empty(o) + return +end + +function test_basic_objective() + _test_basic_objective(MOI.SingleVariable, Float32) + _test_basic_objective(MOI.ScalarAffineFunction{Float32}, Float32) + _test_basic_objective(MOI.ScalarQuadraticFunction{Float32}, Float32) + return +end + +function test_delete_SingleVariable() + o = MOI.Utilities.ObjectiveFunctionContainer{Float16}() + x = MOI.VariableIndex(1234) + f = MOI.SingleVariable(x) + MOI.set(o, MOI.ObjectiveFunction{MOI.SingleVariable}(), f) + MOI.delete(o, x) + @test MOI.is_empty(o) + return +end + +function test_delete_ScalarAffineFunction() + o = MOI.Utilities.ObjectiveFunctionContainer{Float16}() + x = MOI.VariableIndex(1234) + f = convert(MOI.ScalarAffineFunction{Float16}, MOI.SingleVariable(x)) + MOI.set(o, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.delete(o, x) + @test MOI.get(o, MOI.ObjectiveFunctionType()) == typeof(f) + @test_broken !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) + return +end + +function test_delete_ScalarQuadraticFunction() + o = MOI.Utilities.ObjectiveFunctionContainer{Float16}() + x = MOI.VariableIndex(1234) + f = convert(MOI.ScalarQuadraticFunction{Float16}, MOI.SingleVariable(x)) + MOI.set(o, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.delete(o, x) + @test MOI.get(o, MOI.ObjectiveFunctionType()) == typeof(f) + @test_broken !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) + return +end + +function test_delete_SingleVariable_plural() + o = MOI.Utilities.ObjectiveFunctionContainer{Float16}() + x = MOI.VariableIndex(1234) + f = MOI.SingleVariable(x) + MOI.set(o, MOI.ObjectiveFunction{MOI.SingleVariable}(), f) + MOI.delete(o, [x]) + @test MOI.is_empty(o) + return +end + +function test_delete_ScalarAffineFunction_plural() + o = MOI.Utilities.ObjectiveFunctionContainer{Float16}() + x = MOI.VariableIndex(1234) + f = convert(MOI.ScalarAffineFunction{Float16}, MOI.SingleVariable(x)) + MOI.set(o, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.delete(o, [x]) + @test MOI.get(o, MOI.ObjectiveFunctionType()) == typeof(f) + @test_broken !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) + return +end + +function test_delete_ScalarQuadraticFunction_plural() + o = MOI.Utilities.ObjectiveFunctionContainer{Float16}() + x = MOI.VariableIndex(1234) + f = convert(MOI.ScalarQuadraticFunction{Float16}, MOI.SingleVariable(x)) + MOI.set(o, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.delete(o, [x]) + @test MOI.get(o, MOI.ObjectiveFunctionType()) == typeof(f) + @test_broken !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) + return +end + +end # module + +TestObjectiveFunctionContainer.runtests() From da8d75d235e1f624a0711140ab61ba6a41918722 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 22 Jul 2021 16:46:55 +1200 Subject: [PATCH 2/5] Fix tests --- src/Utilities/model.jl | 2 +- src/Utilities/objective_function_container.jl | 2 +- test/Utilities/objective_function_container.jl | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index 9a2cbc068d..cce3160c5a 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -322,7 +322,7 @@ end function MOI.set( model::AbstractModel, - ::MOI.ObjectiveSense, + attr::MOI.ObjectiveSense, sense::MOI.OptimizationSense, ) MOI.set(model.objective, attr, sense) diff --git a/src/Utilities/objective_function_container.jl b/src/Utilities/objective_function_container.jl index 3ae0f7559b..8905ced7e4 100644 --- a/src/Utilities/objective_function_container.jl +++ b/src/Utilities/objective_function_container.jl @@ -4,7 +4,7 @@ A helper struct to simplify the handling of objective functions in Utilities.Model. """ -mutable struct ObjectiveFunctionContainer{T} +mutable struct ObjectiveFunctionContainer{T} <: MOI.ModelLike is_sense_set::Bool sense::MOI.OptimizationSense is_function_set::Bool diff --git a/test/Utilities/objective_function_container.jl b/test/Utilities/objective_function_container.jl index ece292645e..e0df88f4c9 100644 --- a/test/Utilities/objective_function_container.jl +++ b/test/Utilities/objective_function_container.jl @@ -80,7 +80,7 @@ function test_delete_ScalarAffineFunction() MOI.set(o, MOI.ObjectiveFunction{typeof(f)}(), f) MOI.delete(o, x) @test MOI.get(o, MOI.ObjectiveFunctionType()) == typeof(f) - @test_broken !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) + @test !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) return end @@ -91,7 +91,7 @@ function test_delete_ScalarQuadraticFunction() MOI.set(o, MOI.ObjectiveFunction{typeof(f)}(), f) MOI.delete(o, x) @test MOI.get(o, MOI.ObjectiveFunctionType()) == typeof(f) - @test_broken !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) + @test !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) return end @@ -112,7 +112,7 @@ function test_delete_ScalarAffineFunction_plural() MOI.set(o, MOI.ObjectiveFunction{typeof(f)}(), f) MOI.delete(o, [x]) @test MOI.get(o, MOI.ObjectiveFunctionType()) == typeof(f) - @test_broken !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) + @test !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) return end @@ -123,7 +123,7 @@ function test_delete_ScalarQuadraticFunction_plural() MOI.set(o, MOI.ObjectiveFunction{typeof(f)}(), f) MOI.delete(o, [x]) @test MOI.get(o, MOI.ObjectiveFunctionType()) == typeof(f) - @test_broken !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) + @test !(MOI.get(o, MOI.ObjectiveFunction{typeof(f)}()) ≈ f) return end From f48cd8ef9456b19214150430bde42890c1e2fa56 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 22 Jul 2021 17:22:16 +1200 Subject: [PATCH 3/5] Fix tests --- src/Utilities/objective_function_container.jl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Utilities/objective_function_container.jl b/src/Utilities/objective_function_container.jl index 8905ced7e4..a14c747e4f 100644 --- a/src/Utilities/objective_function_container.jl +++ b/src/Utilities/objective_function_container.jl @@ -192,11 +192,13 @@ end function MOI.delete(o::ObjectiveFunctionContainer, x::Vector{MOI.VariableIndex}) keep = v -> !(v in x) - if o.single_variable !== nothing && o.single_variable.variable in x - sense = o.sense - MOI.empty!(o) - if o.is_sense_set - MOI.set(o, MOI.ObjectiveSense(), sense) + if o.single_variable !== nothing + if o.single_variable.variable in x + sense = o.sense + MOI.empty!(o) + if o.is_sense_set + MOI.set(o, MOI.ObjectiveSense(), sense) + end end elseif o.scalar_quadratic !== nothing o.scalar_quadratic = filter_variables(keep, o.scalar_quadratic) From 2024ebecf36d62b89954de7f9e695a56771bdf96 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 23 Jul 2021 10:15:52 +1200 Subject: [PATCH 4/5] Fix variable deletion --- src/Utilities/objective_function_container.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Utilities/objective_function_container.jl b/src/Utilities/objective_function_container.jl index a14c747e4f..763722f6cf 100644 --- a/src/Utilities/objective_function_container.jl +++ b/src/Utilities/objective_function_container.jl @@ -176,10 +176,12 @@ end function MOI.delete(o::ObjectiveFunctionContainer, x::MOI.VariableIndex) if o.single_variable !== nothing - sense = o.sense - MOI.empty!(o) - if o.is_sense_set - MOI.set(o, MOI.ObjectiveSense(), sense) + if x == o.single_variable.variable + sense = o.sense + MOI.empty!(o) + if o.is_sense_set + MOI.set(o, MOI.ObjectiveSense(), sense) + end end elseif o.scalar_quadratic !== nothing o.scalar_quadratic = remove_variable(o.scalar_quadratic, x) From 3d297ff731e33bbc123f7730d83e30346e6c932a Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 27 Jul 2021 12:04:16 +1200 Subject: [PATCH 5/5] Fix UnsupportedAttribute --- src/Utilities/model.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index cce3160c5a..e34cd9b6c4 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -334,6 +334,9 @@ function MOI.set( attr::MOI.ObjectiveFunction{F}, f::F, ) where {F<:MOI.AbstractFunction} + if !MOI.supports(model, attr) + throw(MOI.UnsupportedAttribute(attr)) + end MOI.set(model.objective, attr, f) return end