diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index 04b435764c..b98134588d 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -4,141 +4,221 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. -# Implementation of MOI for AbstractModel abstract type AbstractModelLike{T} <: MOI.ModelLike end -abstract type AbstractOptimizer{T} <: MOI.AbstractOptimizer end -const AbstractModel{T} = Union{AbstractModelLike{T},AbstractOptimizer{T}} - -# Variables -function MOI.get(model::AbstractModel, attr::MOI.NumberOfVariables)::Int64 - return MOI.get(model.variables, attr) -end """ - function _add_variable end + mutable struct GenericModel{T,O,V,C} <: AbstractModelLike{T} -This is called by `AbstractModel` to inform the `constraints` field that a -variable has been added. This is similar to -[`MOI.add_variable`](@ref) except that it should return `nothing`. -""" -function _add_variable end +Implements a model supporting coefficients of type `T` and: -function _add_variable(::Nothing) end -function _add_variables(::Nothing, ::Int64) end + * An objective function stored in `.objective::O` + * Variables and `VariableIndex` constraints stored in `.variable_bounds::V` + * `F`-in-`S` constraints (excluding `VariableIndex` constraints) stored in + `.constraints::C` -function MOI.add_variable(model::AbstractModel) - x = MOI.add_variable(model.variables) - _add_variable(model.constraints) - return x +All interactions take place via the MOI interface, so the types `O`, `V`, and +`C` must implement the API as needed for their functionality. +""" +mutable struct GenericModel{T,O,V,C} <: AbstractModelLike{T} + name::String + objective::O + variables::V + constraints::C + var_to_name::Dict{MOI.VariableIndex,String} + # If `nothing`, the dictionary hasn't been constructed yet. + name_to_var::Union{Dict{String,MOI.VariableIndex},Nothing} + con_to_name::Dict{MOI.ConstraintIndex,String} + name_to_con::Union{Dict{String,MOI.ConstraintIndex},Nothing} + # A useful dictionary for extensions to store things. These are + # _not_ copied between models! + ext::Dict{Symbol,Any} + function GenericModel{T,O,V,C}() where {T,O,V,C} + return new{T,O,V,C}( + "", + O(), + V(), + C(), + Dict{MOI.VariableIndex,String}(), + nothing, + Dict{MOI.ConstraintIndex,String}(), + nothing, + Dict{Symbol,Any}(), + ) + end end -""" - remove_variable(f::MOI.AbstractFunction, s::MOI.AbstractSet, vi::MOI.VariableIndex) +abstract type AbstractOptimizer{T} <: MOI.AbstractOptimizer end -Return a tuple `(g, t)` representing the constraint `f`-in-`s` with the -variable `vi` removed. That is, the terms containing the variable `vi` in the -function `f` are removed and the dimension of the set `s` is updated if -needed (e.g. when `f` is a `VectorOfVariables` with `vi` being one of the -variables). """ -remove_variable(f, s, vi::MOI.VariableIndex) = remove_variable(f, vi), s + mutable struct GenericOptimizer{T,O,V,C} <: AbstractOptimizer{T} -function remove_variable(f::MOI.VectorOfVariables, s, vi::MOI.VariableIndex) - g = remove_variable(f, vi) - if length(g.variables) != length(f.variables) - t = MOI.update_dimension(s, length(g.variables)) - else - t = s +Implements a model supporting coefficients of type `T` and: + + * An objective function stored in `.objective::O` + * Variables and `VariableIndex` constraints stored in `.variable_bounds::V` + * `F`-in-`S` constraints (excluding `VariableIndex` constraints) stored in + `.constraints::C` + +All interactions take place via the MOI interface, so the types `O`, `V`, and +`C` must implement the API as needed for their functionality. +""" +mutable struct GenericOptimizer{T,O,V,C} <: AbstractOptimizer{T} + name::String + objective::O + variables::V + constraints::C + var_to_name::Dict{MOI.VariableIndex,String} + # If `nothing`, the dictionary hasn't been constructed yet. + name_to_var::Union{Dict{String,MOI.VariableIndex},Nothing} + con_to_name::Dict{MOI.ConstraintIndex,String} + name_to_con::Union{Dict{String,MOI.ConstraintIndex},Nothing} + # A useful dictionary for extensions to store things. These are + # _not_ copied between models! + ext::Dict{Symbol,Any} + function GenericOptimizer{T,O,V,C}() where {T,O,V,C} + return new{T,O,V,C}( + "", + O(), + V(), + C(), + Dict{MOI.VariableIndex,String}(), + nothing, + Dict{MOI.ConstraintIndex,String}(), + nothing, + Dict{Symbol,Any}(), + ) end - return g, t end -function filter_variables(keep::F, f, s) where {F<:Function} - return filter_variables(keep, f), s +const AbstractModel{T} = Union{AbstractModelLike{T},AbstractOptimizer{T}} + +# `AbstractModel` fields `.variables` and `.constraints` act like a +# `StructOfConstraints` where `.variables` contains the `VariableIndex`-in-`S` +# constraints and `.constraints` contains the other constraints. +function constraints( + model::AbstractModel, + ::MOI.ConstraintIndex{MOI.VariableIndex}, +) + return model.variables end -function filter_variables( - keep::F, - f::MOI.VectorOfVariables, - s, -) where {F<:Function} - g = filter_variables(keep, f) - if length(g.variables) != length(f.variables) - t = MOI.update_dimension(s, length(g.variables)) - else - t = s - end - return g, t + +constraints(model::AbstractModel, ::MOI.ConstraintIndex) = model.constraints + +function MOI.is_empty(model::AbstractModel) + return isempty(model.name) && + MOI.is_empty(model.objective) && + MOI.is_empty(model.constraints) && + MOI.is_empty(model.variables) end -function _delete_variable( - model::AbstractModel{T}, - vi::MOI.VariableIndex, -) where {T} - MOI.delete(model.variables, vi) - delete!(model.var_to_name, vi) +function MOI.empty!(model::AbstractModel{T}) where {T} + model.name = "" + MOI.empty!(model.objective) + MOI.empty!(model.variables) + MOI.empty!(model.constraints) + empty!(model.var_to_name) model.name_to_var = nothing + empty!(model.con_to_name) model.name_to_con = nothing return end -function MOI.delete(model::AbstractModel, vi::MOI.VariableIndex) - _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 - MOI.delete(model.objective, vi) - model.name_to_con = nothing - return +function MOI.copy_to(dest::AbstractModel, src::MOI.ModelLike) + return default_copy_to(dest, src) end -function MOI.delete(model::AbstractModel, vis::Vector{MOI.VariableIndex}) - if isempty(vis) - return - end - _throw_if_cannot_delete(model.constraints, vis, Set(vis)) - _deleted_constraints(model.constraints, vis) do ci - return delete!(model.con_to_name, ci) +MOI.supports_incremental_interface(::AbstractModel) = true + +function final_touch(model::AbstractModel, index_map) + return final_touch(model.constraints, index_map) +end + +# MOI.ListOfXXXAttributesSet + +function MOI.get(::AbstractModel, ::MOI.ListOfOptimizerAttributesSet) + return MOI.AbstractOptimizerAttribute[] +end + +function MOI.get( + model::AbstractModel, + attr::MOI.ListOfModelAttributesSet, +)::Vector{MOI.AbstractModelAttribute} + ret = MOI.AbstractModelAttribute[] + append!(ret, MOI.get(model.objective, attr)) + if !isempty(model.name) + push!(ret, MOI.Name()) end - for vi in vis - _delete_variable(model, vi) + return ret +end + +function MOI.get(model::AbstractModel, ::MOI.ListOfVariableAttributesSet) + ret = MOI.AbstractVariableAttribute[] + if isempty(model.var_to_name) + push!(ret, MOI.VariableName()) end - MOI.delete(model.objective, vis) - model.name_to_con = nothing - return + return ret end -# `AbstractModel` fields `.variables` and `.constraints` act like a -# `StructOfConstraints` where `.variables` contains the `VariableIndex`-in-`S` -# constraints and `.constraints` contains the other constraints. -function constraints( +function MOI.get( model::AbstractModel, - ci::MOI.ConstraintIndex{MOI.VariableIndex}, -) - return model.variables + ::MOI.ListOfConstraintAttributesSet{F,S}, +) where {F,S} + ret = MOI.AbstractConstraintAttribute[] + if any(Base.Fix2(isa, MOI.ConstraintIndex{F,S}), keys(model.con_to_name)) + push!(ret, MOI.ConstraintName()) + end + return ret end -function constraints(model::AbstractModel, ci::MOI.ConstraintIndex) - return model.constraints + +# MOI.Name + +MOI.supports(::AbstractModel, ::MOI.Name) = true + +function MOI.set(model::AbstractModel, ::MOI.Name, name::String) + model.name = name + return end -function MOI.is_valid(model::AbstractModel, ci::MOI.ConstraintIndex) - return MOI.is_valid(constraints(model, ci), ci) +MOI.get(model::AbstractModel, ::MOI.Name) = model.name + +# MOI.add_variable + +""" + function _add_variable end + +This is called by `AbstractModel` to inform the `constraints` field that a +variable has been added. This is similar to +[`MOI.add_variable`](@ref) except that it should return `nothing`. +""" +function _add_variable end + +_add_variable(::Nothing) = nothing + +_add_variables(::Nothing, ::Int64) = nothing + +function MOI.add_variable(model::AbstractModel) + x = MOI.add_variable(model.variables) + _add_variable(model.constraints) + return x end function MOI.is_valid(model::AbstractModel, x::MOI.VariableIndex) return MOI.is_valid(model.variables, x) end -function MOI.get(model::AbstractModel, attr::MOI.ListOfVariableIndices) +# MOI.NumberOfVariables + +function MOI.get(model::AbstractModel, attr::MOI.NumberOfVariables)::Int64 return MOI.get(model.variables, attr) end -# Names -MOI.supports(::AbstractModel, ::MOI.Name) = true -function MOI.set(model::AbstractModel, ::MOI.Name, name::String) - return model.name = name +# MOI.ListOfVariableIndices + +function MOI.get(model::AbstractModel, attr::MOI.ListOfVariableIndices) + return MOI.get(model.variables, attr) end -MOI.get(model::AbstractModel, ::MOI.Name) = model.name + +# MOI.VariableName function MOI.supports( ::AbstractModel, @@ -167,6 +247,8 @@ function MOI.get( return get(model.var_to_name, vi, "") end +# MOI.get(::AbstractModel, ::Type{MOI.VariableIndex}, ::String) + """ build_name_to_var_map(con_to_name::Dict{MOI.VariableIndex, String}) @@ -191,19 +273,22 @@ end function throw_multiple_name_error(::Type{MOI.VariableIndex}, name::String) return error("Multiple variables have the name $name.") end + function throw_multiple_name_error(::Type{<:MOI.ConstraintIndex}, name::String) return error("Multiple constraints have the name $name.") end + function throw_if_multiple_with_name(::Nothing, ::String) end + function throw_if_multiple_with_name(index::MOI.Index, name::String) if iszero(index.value) throw_multiple_name_error(typeof(index), name) end + return end function MOI.get(model::AbstractModel, ::Type{MOI.VariableIndex}, name::String) if model.name_to_var === nothing - # Rebuild the map. model.name_to_var = build_name_to_var_map(model.var_to_name) end result = get(model.name_to_var, name, nothing) @@ -211,13 +296,110 @@ function MOI.get(model::AbstractModel, ::Type{MOI.VariableIndex}, name::String) return result end +# Constraints + +function MOI.is_valid(model::AbstractModel, ci::MOI.ConstraintIndex) + return MOI.is_valid(constraints(model, ci), ci) +end + +function MOI.supports_constraint( + model::AbstractModel, + ::Type{F}, + ::Type{S}, +) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet} + return MOI.supports_constraint(model.constraints, F, S) +end + +function MOI.add_constraint( + model::AbstractModel, + func::MOI.AbstractFunction, + set::MOI.AbstractSet, +) + return MOI.add_constraint(model.constraints, func, set) +end + +function MOI.supports_constraint( + model::AbstractModel, + ::Type{MOI.VariableIndex}, + ::Type{S}, +) where {S<:MOI.AbstractScalarSet} + return MOI.supports_constraint(model.variables, MOI.VariableIndex, S) +end + +function MOI.add_constraint( + model::AbstractModel, + f::MOI.VariableIndex, + s::MOI.AbstractScalarSet, +) + return MOI.add_constraint(model.variables, f, s) +end + +# MOI.NumberOfConstraints + function MOI.get( model::AbstractModel, - ::MOI.ListOfVariableAttributesSet, -)::Vector{MOI.AbstractVariableAttribute} - return isempty(model.var_to_name) ? [] : [MOI.VariableName()] + attr::MOI.NumberOfConstraints{MOI.VariableIndex,S}, +)::Int64 where {S<:MOI.AbstractScalarSet} + if !MOI.supports_constraint(model, MOI.VariableIndex, S) + return 0 + end + return MOI.get(model.variables, attr) end +function MOI.get(model::AbstractModel, attr::MOI.NumberOfConstraints)::Int64 + return MOI.get(model.constraints, attr) +end + +# MOI.ListOfConstraintTypesPresent + +function MOI.get( + model::AbstractModel{T}, + attr::MOI.ListOfConstraintTypesPresent, +) where {T} + return vcat( + MOI.get(model.constraints, attr)::Vector{Tuple{Type,Type}}, + MOI.get(model.variables, attr)::Vector{Tuple{Type,Type}}, + ) +end + +# MOI.ListOfConstraintIndices + +function MOI.get( + model::AbstractModel, + attr::MOI.ListOfConstraintIndices{MOI.VariableIndex,S}, +) where {S<:MOI.AbstractScalarSet} + if !MOI.supports_constraint(model, MOI.VariableIndex, S) + return MOI.ConstraintIndex{MOI.VariableIndex,S}[] + end + return MOI.get(model.variables, attr) +end + +function MOI.get(model::AbstractModel, attr::MOI.ListOfConstraintIndices) + return MOI.get(model.constraints, attr) +end + +# MOI.ConstraintFunction/MOI.ConstraintSet + +function MOI.get( + model::AbstractModel, + attr::Union{MOI.ConstraintFunction,MOI.ConstraintSet}, + ci::MOI.ConstraintIndex, +) + return MOI.get(constraints(model, ci), attr, ci) +end + +function MOI.set( + model::AbstractModel, + attr::Union{MOI.ConstraintFunction,MOI.ConstraintSet}, + ci::MOI.ConstraintIndex, + value, +) + MOI.set(constraints(model, ci), attr, ci, value) + return +end + +# MOI.ConstraintName + function MOI.supports( ::AbstractModel, ::MOI.ConstraintName, @@ -237,6 +419,14 @@ function MOI.set( return end +function MOI.get( + model::AbstractModel, + ::MOI.ConstraintName, + ci::MOI.ConstraintIndex, +) + return get(model.con_to_name, ci, "") +end + function MOI.supports( ::AbstractModel, ::MOI.ConstraintName, @@ -254,13 +444,7 @@ function MOI.set( return throw(MOI.VariableIndexConstraintNameError()) end -function MOI.get( - model::AbstractModel, - ::MOI.ConstraintName, - ci::MOI.ConstraintIndex, -) - return get(model.con_to_name, ci, "") -end +# MOI.get(::AbstractModel, ::Type{MOI.ConstraintIndex}, ::String) """ build_name_to_con_map(con_to_name::Dict{MOI.ConstraintIndex, String}) @@ -288,7 +472,6 @@ function MOI.get( name::String, ) where {ConType<:MOI.ConstraintIndex} if model.name_to_con === nothing - # Rebuild the map. model.name_to_con = build_name_to_con_map(model.con_to_name) end ci = get(model.name_to_con, name, nothing) @@ -296,36 +479,22 @@ function MOI.get( return ci isa ConType ? ci : nothing end -function MOI.get( - model::AbstractModel, - ::MOI.ListOfConstraintAttributesSet{F,S}, -) where {F,S} - if any(k -> k isa MOI.ConstraintIndex{F,S}, keys(model.con_to_name)) - return MOI.AbstractConstraintAttribute[MOI.ConstraintName()] - end - return MOI.AbstractConstraintAttribute[] -end - -# Objective +# MOI.ObjectiveFunctionType -function MOI.get( - model::AbstractModel, - attr::Union{ - MOI.ObjectiveSense, - MOI.ObjectiveFunction, - MOI.ObjectiveFunctionType, - }, -) +function MOI.get(model::AbstractModel, attr::MOI.ObjectiveFunctionType) return MOI.get(model.objective, attr) end -function MOI.supports( - model::AbstractModel, - attr::Union{MOI.ObjectiveSense,MOI.ObjectiveFunction}, -) +# MOI.ObjectiveSense + +function MOI.supports(model::AbstractModel, attr::MOI.ObjectiveSense) return MOI.supports(model.objective, attr) end +function MOI.get(model::AbstractModel, attr::MOI.ObjectiveSense) + return MOI.get(model.objective, attr) +end + function MOI.set( model::AbstractModel, attr::MOI.ObjectiveSense, @@ -335,6 +504,16 @@ function MOI.set( return end +# MOI.ObjectiveFunction + +function MOI.supports(model::AbstractModel, attr::MOI.ObjectiveFunction) + return MOI.supports(model.objective, attr) +end + +function MOI.get(model::AbstractModel, attr::MOI.ObjectiveFunction) + return MOI.get(model.objective, attr) +end + function MOI.set( model::AbstractModel, attr::MOI.ObjectiveFunction{F}, @@ -347,6 +526,8 @@ function MOI.set( return end +# MOI.modify + function MOI.modify( model::AbstractModel, attr::MOI.ObjectiveFunction, @@ -356,62 +537,17 @@ function MOI.modify( return end -function MOI.get(::AbstractModel, ::MOI.ListOfOptimizerAttributesSet) - return MOI.AbstractOptimizerAttribute[] -end - -function MOI.get( - model::AbstractModel, - attr::MOI.ListOfModelAttributesSet, -)::Vector{MOI.AbstractModelAttribute} - ret = MOI.get(model.objective, attr) - if !isempty(model.name) - push!(ret, MOI.Name()) - end - return ret -end - -# Constraints - -function MOI.supports_constraint( - model::AbstractModel, - ::Type{MOI.VariableIndex}, - ::Type{S}, -) where {S<:MOI.AbstractScalarSet} - return MOI.supports_constraint(model.variables, MOI.VariableIndex, S) -end -function MOI.supports_constraint( - model::AbstractModel, - ::Type{F}, - ::Type{S}, -) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet} - return MOI.supports_constraint(model.constraints, F, S) -end - -function MOI.add_constraint( - model::AbstractModel, - f::MOI.VariableIndex, - s::MOI.AbstractScalarSet, -) - return MOI.add_constraint(model.variables, f, s) -end - -function MOI.add_constraint( - model::AbstractModel, - func::MOI.AbstractFunction, - set::MOI.AbstractSet, -) - return MOI.add_constraint(model.constraints, func, set) -end - -function MOI.get( +function MOI.modify( model::AbstractModel, - attr::Union{MOI.AbstractFunction,MOI.AbstractSet}, ci::MOI.ConstraintIndex, + change::MOI.AbstractFunctionModification, ) - return MOI.get(model.constraints, attr, ci) + MOI.modify(model.constraints, ci, change) + return end +# MOI.delete + function MOI.delete(model::AbstractModel, ci::MOI.ConstraintIndex) MOI.delete(constraints(model, ci), ci) model.name_to_con = nothing @@ -419,96 +555,39 @@ function MOI.delete(model::AbstractModel, ci::MOI.ConstraintIndex) return end -function MOI.modify( - model::AbstractModel, - ci::MOI.ConstraintIndex, - change::MOI.AbstractFunctionModification, -) - MOI.modify(model.constraints, ci, change) - return -end - -function MOI.set( - model::AbstractModel, - attr::Union{MOI.ConstraintFunction,MOI.ConstraintSet}, - ci::MOI.ConstraintIndex, - value, -) - MOI.set(constraints(model, ci), attr, ci, value) +function MOI.delete(model::AbstractModel, vi::MOI.VariableIndex) + _throw_if_cannot_delete(model.constraints, [vi], vi) + MOI.delete(model.variables, vi) + delete!(model.var_to_name, vi) + _deleted_constraints(model.constraints, vi) do ci + return delete!(model.con_to_name, ci) + end + MOI.delete(model.objective, vi) + model.name_to_var = nothing + model.name_to_con = nothing return end -function MOI.get( - model::AbstractModel, - attr::MOI.NumberOfConstraints{MOI.VariableIndex,S}, -)::Int64 where {S} - if !MOI.supports_constraint(model, MOI.VariableIndex, S) - return 0 +function MOI.delete(model::AbstractModel, vis::Vector{MOI.VariableIndex}) + if isempty(vis) + return end - return MOI.get(model.variables, attr) -end - -function MOI.get( - model::AbstractModel, - noc::MOI.NumberOfConstraints{F,S}, -)::Int64 where {F,S} - return MOI.get(model.constraints, noc) -end - -function MOI.get( - model::AbstractModel{T}, - attr::MOI.ListOfConstraintTypesPresent, -) where {T} - return vcat( - MOI.get(model.constraints, attr)::Vector{Tuple{Type,Type}}, - MOI.get(model.variables, attr)::Vector{Tuple{Type,Type}}, - ) -end - -function MOI.get( - model::AbstractModel, - attr::MOI.ListOfConstraintIndices{MOI.VariableIndex,S}, -) where {S} - if !MOI.supports_constraint(model, MOI.VariableIndex, S) - return MOI.ConstraintIndex{MOI.VariableIndex,S}[] + _throw_if_cannot_delete(model.constraints, vis, Set(vis)) + _deleted_constraints(model.constraints, vis) do ci + return delete!(model.con_to_name, ci) end - return MOI.get(model.variables, attr) -end - -function MOI.get( - model::AbstractModel, - loc::MOI.ListOfConstraintIndices{F,S}, -) where {F,S} - return MOI.get(model.constraints, loc) -end - -function MOI.get( - model::AbstractModel, - attr::Union{MOI.ConstraintFunction,MOI.ConstraintSet}, - ci::MOI.ConstraintIndex, -) - return MOI.get(constraints(model, ci), attr, ci) -end - -function MOI.is_empty(model::AbstractModel) - return isempty(model.name) && - MOI.is_empty(model.objective) && - MOI.is_empty(model.constraints) && - MOI.is_empty(model.variables) -end - -function MOI.empty!(model::AbstractModel{T}) where {T} - model.name = "" - MOI.empty!(model.objective) - MOI.empty!(model.variables) - empty!(model.var_to_name) + for vi in vis + MOI.delete(model.variables, vi) + delete!(model.var_to_name, vi) + end + MOI.delete(model.objective, vis) model.name_to_var = nothing - empty!(model.con_to_name) model.name_to_con = nothing - MOI.empty!(model.constraints) return end +# A copy utility + function pass_nonvariable_constraints( dest::AbstractModel, src::MOI.ModelLike, @@ -523,15 +602,6 @@ function pass_nonvariable_constraints( ) end -function MOI.copy_to(dest::AbstractModel, src::MOI.ModelLike) - return default_copy_to(dest, src) -end - -MOI.supports_incremental_interface(::AbstractModel) = true -function final_touch(model::AbstractModel, index_map) - return final_touch(model.constraints, index_map) -end - # Macro to generate Model function _struct_of_constraints_type(name, subtypes, parametrized_type) @@ -729,57 +799,6 @@ macro model( return expr end -for (loop_name, loop_super_type) in [ - (:GenericModel, :AbstractModelLike), - (:GenericOptimizer, :AbstractOptimizer), -] - global name = loop_name - global super_type = loop_super_type - @eval begin - """ - mutable struct $name{T,O,V,C} <: $super_type{T} - - Implements a model supporting coefficients of type `T` and: - - * An objective function stored in `.objective::O` - * Variables and `VariableIndex` constraints stored in `.variable_bounds::V` - * `F`-in-`S` constraints (excluding `VariableIndex` constraints) - stored in `.constraints::C` - - All interactions should take place via the MOI interface, so the types - `O`, `V`, and `C` should implement the API as needed for their - functionality. - """ - mutable struct $name{T,O,V,C} <: $super_type{T} - name::String - objective::O - variables::V - constraints::C - var_to_name::Dict{MOI.VariableIndex,String} - # If `nothing`, the dictionary hasn't been constructed yet. - name_to_var::Union{Dict{String,MOI.VariableIndex},Nothing} - con_to_name::Dict{MOI.ConstraintIndex,String} - name_to_con::Union{Dict{String,MOI.ConstraintIndex},Nothing} - # A useful dictionary for extensions to store things. These are - # _not_ copied between models! - ext::Dict{Symbol,Any} - function $name{T,O,V,C}() where {T,O,V,C} - return new{T,O,V,C}( - "", - O(), - V(), - C(), - Dict{MOI.VariableIndex,String}(), - nothing, - Dict{MOI.ConstraintIndex,String}(), - nothing, - Dict{Symbol,Any}(), - ) - end - end - end -end - const LessThanIndicatorOne{T} = MOI.Indicator{MOI.ACTIVATE_ON_ONE,MOI.LessThan{T}} const LessThanIndicatorZero{T} = @@ -849,14 +868,18 @@ const LessThanIndicatorZero{T} = ) @doc raw""" + MOI.Utilities.Model{T}() where {T} An implementation of `ModelLike` that supports all functions and sets defined in MOI. It is parameterized by the coefficient type. -# Examples -```jl -model = Model{Float64}() -x = add_variable(model) +## Examples + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}() +MOIU.Model{Float64} ``` """ Model @@ -865,8 +888,10 @@ Model # ```julia # julia> Base.show(IOContext(stdout, :compact => true), MathOptInterface.Utilities.Model) # Model{T} where T +# # julia> print(MathOptInterface.Utilities.Model) # MathOptInterface.Utilities.Model{T} where T +# # julia> MathOptInterface.Utilities.Model # MathOptInterface.Utilities.Model{T} where T (alias for MathOptInterface.Utilities.GenericModel{T, MathOptInterface.Utilities.ObjectiveContainer{T}, MathOptInterface.Utilities.VariablesContainer{T}, MathOptInterface.Utilities.ModelFunctionConstraints{T}} where T) # ``` diff --git a/src/Utilities/vector_of_constraints.jl b/src/Utilities/vector_of_constraints.jl index 6033121118..3a0b272ac7 100644 --- a/src/Utilities/vector_of_constraints.jl +++ b/src/Utilities/vector_of_constraints.jl @@ -176,6 +176,29 @@ function _add_variables(::VectorOfConstraints, ::Int64) end # Deletion of variables in vector of variables +""" + remove_variable( + f::MOI.AbstractFunction, + s::MOI.AbstractSet, + vi::MOI.VariableIndex, + ) + +Return a tuple `(g, t)` representing the constraint `f`-in-`s` with the +variable `vi` removed. That is, the terms containing the variable `vi` in the +function `f` are removed and the dimension of the set `s` is updated if +needed (e.g. when `f` is a `VectorOfVariables` with `vi` being one of the +variables). +""" +remove_variable(f, s, vi::MOI.VariableIndex) = remove_variable(f, vi), s + +function remove_variable(f::MOI.VectorOfVariables, s, vi::MOI.VariableIndex) + g = remove_variable(f, vi) + if length(g.variables) != length(f.variables) + return g, MOI.update_dimension(s, length(g.variables)) + end + return g, s +end + function _remove_variable(v::VectorOfConstraints, vi::MOI.VariableIndex) CleverDicts.map_values!(v.constraints) do (f, s) return remove_variable(f, s, vi) @@ -183,6 +206,22 @@ function _remove_variable(v::VectorOfConstraints, vi::MOI.VariableIndex) return end +function filter_variables(keep::F, f, s) where {F<:Function} + return filter_variables(keep, f), s +end + +function filter_variables( + keep::F, + f::MOI.VectorOfVariables, + s, +) where {F<:Function} + g = filter_variables(keep, f) + if length(g.variables) != length(f.variables) + return g, MOI.update_dimension(s, length(g.variables)) + end + return g, s +end + function _filter_variables(keep::Function, v::VectorOfConstraints) CleverDicts.map_values!(v.constraints) do (f, s) return filter_variables(keep, f, s)