diff --git a/src/Utilities/copy.jl b/src/Utilities/copy.jl index bd98639c95..e30be80752 100644 --- a/src/Utilities/copy.jl +++ b/src/Utilities/copy.jl @@ -189,6 +189,9 @@ function _copy_constraints( index_map, cis_src::Vector{MOI.ConstraintIndex{F,S}}, ) where {F,S} + if !MOI.supports_constraint(dest, F, S) + throw(MOI.UnsupportedConstraint{F,S}()) + end return _copy_constraints(dest, src, index_map, index_map[F, S], cis_src) end @@ -383,19 +386,6 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike) error("Model $(typeof(dest)) does not support copy_to.") end MOI.empty!(dest) - for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) - if F == MOI.VariableIndex - if !MOI.supports_add_constrained_variable(dest, S) - throw(MOI.UnsupportedConstraint{F,S}()) - end - elseif F == MOI.VectorOfVariables - if !MOI.supports_add_constrained_variables(dest, S) - throw(MOI.UnsupportedConstraint{F,S}()) - end - elseif !MOI.supports_constraint(dest, F, S) - throw(MOI.UnsupportedConstraint{F,S}()) - end - end index_map, vis_src, constraints_not_added = _copy_variables_with_set(dest, src) # Copy variable attributes @@ -424,13 +414,21 @@ struct _CopyVariablesWithSetCache end function _build_copy_variables_with_set_cache( + dest::MOI.ModelLike, src::MOI.ModelLike, cache::_CopyVariablesWithSetCache, ::Type{S}, ) where {S<:MOI.AbstractScalarSet} F = MOI.VariableIndex + supports = + MOI.supports_add_constrained_variable(dest, S) || + MOI.supports_constraint(dest, F, S) indices = MOI.ConstraintIndex{F,S}[] for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) + # Check this inside the loop so there exists at least one constraint + if !supports + throw(MOI.UnsupportedConstraint{F,S}()) + end x = MOI.get(src, MOI.ConstraintFunction(), ci) if x in cache.variables_with_domain # `x` is already assigned to a domain. Add this constraint via @@ -479,13 +477,21 @@ function _is_variable_cone( end function _build_copy_variables_with_set_cache( + dest::MOI.ModelLike, src::MOI.ModelLike, cache::_CopyVariablesWithSetCache, ::Type{S}, ) where {S<:MOI.AbstractVectorSet} F = MOI.VectorOfVariables + supports = + MOI.supports_add_constrained_variables(dest, S) || + MOI.supports_constraint(dest, F, S) indices = MOI.ConstraintIndex{F,S}[] for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) + # Check this inside the loop so there exists at least one constraint + if !supports + throw(MOI.UnsupportedConstraint{F,S}()) + end f = MOI.get(src, MOI.ConstraintFunction(), ci) if _is_variable_cone(cache, f) for fi in f.variables @@ -544,7 +550,7 @@ function _copy_variables_with_set(dest, src) cache.variable_to_column[v] = i end for S in sorted_variable_sets_by_cost(dest, src) - _build_copy_variables_with_set_cache(src, cache, S) + _build_copy_variables_with_set_cache(dest, src, cache, S) end column(x::MOI.VariableIndex) = cache.variable_to_column[x] start_column(x) = column(first(x[1])) @@ -715,9 +721,14 @@ function _try_constrain_variables_on_creation( index_map::IndexMap, ::Type{S}, ) where {S<:MOI.AbstractVectorSet} + supports = MOI.supports_add_constrained_variables(dest, S) not_added = MOI.ConstraintIndex{MOI.VectorOfVariables,S}[] for ci_src in MOI.get(src, MOI.ListOfConstraintIndices{MOI.VectorOfVariables,S}()) + # Check this inside the loop so there exists at least one constraint + if !supports + throw(MOI.UnsupportedConstraint{MOI.VectorOfVariables,S}()) + end f_src = MOI.get(src, MOI.ConstraintFunction(), ci_src) if !allunique(f_src.variables) # Can't add it because there are duplicate variables @@ -743,9 +754,14 @@ function _try_constrain_variables_on_creation( index_map::IndexMap, ::Type{S}, ) where {S<:MOI.AbstractScalarSet} + supports = MOI.supports_add_constrained_variable(dest, S) not_added = MOI.ConstraintIndex{MOI.VariableIndex,S}[] for ci_src in MOI.get(src, MOI.ListOfConstraintIndices{MOI.VariableIndex,S}()) + # Check this inside the loop so there exists at least one constraint + if !supports + throw(MOI.UnsupportedConstraint{MOI.VariableIndex,S}()) + end f_src = MOI.get(src, MOI.ConstraintFunction(), ci_src) if haskey(index_map, f_src) # Can't add it because it contains a variable previously added diff --git a/test/Utilities/copy.jl b/test/Utilities/copy.jl index 7d14f1b399..bdecac30c3 100644 --- a/test/Utilities/copy.jl +++ b/test/Utilities/copy.jl @@ -176,12 +176,8 @@ MOI.empty!(model::ConstrainedVariablesModel) = empty!(model.added_constrained) MOI.supports_incremental_interface(::ConstrainedVariablesModel) = true -function MOI.copy_to( - dest::ConstrainedVariablesModel, - src::MOI.ModelLike; - kwargs..., -) - return MOIU.default_copy_to(dest, src; kwargs...) +function MOI.copy_to(dest::ConstrainedVariablesModel, src::MOI.ModelLike) + return MOIU.default_copy_to(dest, src) end function MOI.add_variables(model::ConstrainedVariablesModel, n) @@ -192,6 +188,13 @@ function MOI.add_variables(model::ConstrainedVariablesModel, n) return MOI.VariableIndex.(m .+ (1:n)) end +function MOI.supports_add_constrained_variables( + ::ConstrainedVariablesModel, + ::Type{<:MOI.AbstractVectorSet}, +) + return true +end + function MOI.add_constrained_variables( model::ConstrainedVariablesModel, set::MOI.AbstractVectorSet, @@ -204,6 +207,14 @@ function MOI.add_constrained_variables( return MOI.VariableIndex.(m .+ (1:MOI.dimension(set))), ci end +function MOI.supports_constraint( + ::ConstrainedVariablesModel, + ::Type{MOI.VectorOfVariables}, + ::Type{<:MOI.AbstractVectorSet}, +) + return true +end + function MOI.add_constraint( ::ConstrainedVariablesModel, func::MOI.VectorOfVariables, @@ -261,6 +272,14 @@ function MOI.add_variable(model::AbstractConstrainedVariablesModel) return MOI.add_variable(model.inner) end +function MOI.supports_constraint( + ::AbstractConstrainedVariablesModel, + ::Type{<:MOI.AbstractFunction}, + ::Type{<:MOI.AbstractSet}, +) + return true +end + function MOI.add_constraint( model::AbstractConstrainedVariablesModel, f::MOI.AbstractFunction, @@ -273,10 +292,9 @@ end function MOI.copy_to( dest::AbstractConstrainedVariablesModel, - src::MOI.ModelLike; - kwargs..., + src::MOI.ModelLike, ) - return MOIU.default_copy_to(dest, src; kwargs...) + return MOIU.default_copy_to(dest, src) end MOI.supports_incremental_interface(::AbstractConstrainedVariablesModel) = true @@ -321,7 +339,7 @@ function MOI.supports_constraint( ::Type{MOI.VectorOfVariables}, ::Type{MOI.Nonnegatives}, ) - return false + return true end function MOI.supports_add_constrained_variables( @@ -1003,32 +1021,32 @@ function test_copy_to_sorted_sets() return end -function test_add_constraint_not_allowed_scalar_variable() +function test_default_copy_to_unsupported_scalar_variable() F, S = MOI.VariableIndex, MOI.GreaterThan{Float64} model = MOI.Utilities.Model{Float64}() x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(1.0)) dest = MOI.FileFormats.CBF.Model() - @test_throws(MOI.AddConstraintNotAllowed{F,S}, MOI.copy_to(dest, model)) + @test_throws(MOI.UnsupportedConstraint{F,S}, MOI.copy_to(dest, model)) return end -function test_add_constraint_not_allowed_vector_variables() +function test_default_copy_to_unsupported_vector_variables() F, S = MOI.VectorOfVariables, MOI.AllDifferent model = MOI.Utilities.Model{Float64}() x, _ = MOI.add_constrained_variables(model, MOI.AllDifferent(3)) dest = MOI.FileFormats.CBF.Model() - @test_throws(MOI.AddConstraintNotAllowed{F,S}, MOI.copy_to(dest, model)) + @test_throws(MOI.UnsupportedConstraint{F,S}, MOI.copy_to(dest, model)) return end -function test_add_constraint_not_allowed_scalar_function() +function test_default_copy_to_unsupported_scalar_function() F, S = MOI.ScalarNonlinearFunction, MOI.EqualTo{Float64} model = MOI.Utilities.Model{Float64}() x = MOI.add_variable(model) f = MOI.ScalarNonlinearFunction(:log, Any[x]) MOI.add_constraint(model, f, MOI.EqualTo(0.0)) dest = MOI.FileFormats.CBF.Model() - @test_throws(MOI.AddConstraintNotAllowed{F,S}, MOI.copy_to(dest, model)) + @test_throws(MOI.UnsupportedConstraint{F,S}, MOI.copy_to(dest, model)) return end