diff --git a/src/Utilities/matrix_of_constraints.jl b/src/Utilities/matrix_of_constraints.jl index 921dd820ac..797aa8ef55 100644 --- a/src/Utilities/matrix_of_constraints.jl +++ b/src/Utilities/matrix_of_constraints.jl @@ -92,13 +92,24 @@ mutable struct MatrixOfConstraints{T,AT,BT,ST} <: MOI.ModelLike caches::Vector{Any} are_indices_mapped::Vector{BitSet} final_touch::Bool - function MatrixOfConstraints{T,AT,BT,ST}() where {T,AT,BT,ST} - model = new{T,AT,BT,ST}(AT(), BT(), ST(), Any[], BitSet[], false) - MOI.empty!(model) + function MatrixOfConstraints{T}(coefficients, constants, sets) where {T} + model = new{T,typeof(coefficients),typeof(constants),typeof(sets)}( + coefficients, + constants, + sets, + Any[], + BitSet[], + false, + ) + _reset_caches!(model) return model end end +function MatrixOfConstraints{T,AT,BT,ST}() where {T,AT,BT,ST} + return MatrixOfConstraints{T}(AT(), BT(), ST()) +end + ### ### Interface for the .coefficients field ### @@ -292,13 +303,17 @@ function rows end MOI.is_empty(v::MatrixOfConstraints) = MOI.is_empty(v.sets) -function MOI.empty!(v::MatrixOfConstraints{T}) where {T} +function _reset_caches!(v::MatrixOfConstraints{T}) where {T} + v.caches = + [Tuple{_affine_function_type(T, S),S}[] for S in set_types(v.sets)] + return v.are_indices_mapped = [BitSet() for _ in eachindex(v.caches)] +end + +function MOI.empty!(v::MatrixOfConstraints) MOI.empty!(v.coefficients) empty!(v.constants) MOI.empty!(v.sets) - v.caches = - [Tuple{_affine_function_type(T, S),S}[] for S in set_types(v.sets)] - v.are_indices_mapped = [BitSet() for _ in eachindex(v.caches)] + _reset_caches!(v) v.final_touch = false return end diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index 184488c3b8..06ef912a3d 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -32,12 +32,12 @@ mutable struct GenericModel{T,O,V,C} <: AbstractModelLike{T} # 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}( + function GenericModel{T}(objective, variables, constraints) where {T} + return new{T,typeof(objective),typeof(variables),typeof(constraints)}( "", - O(), - V(), - C(), + objective, + variables, + constraints, Dict{MOI.VariableIndex,String}(), nothing, Dict{MOI.ConstraintIndex,String}(), @@ -47,6 +47,10 @@ mutable struct GenericModel{T,O,V,C} <: AbstractModelLike{T} end end +function GenericModel{T,O,V,C}() where {T,O,V,C} + return GenericModel{T}(O(), V(), C()) +end + abstract type AbstractOptimizer{T} <: MOI.AbstractOptimizer end """ diff --git a/src/Utilities/sparse_matrix.jl b/src/Utilities/sparse_matrix.jl index 98b4b4195c..fd5b37f41a 100644 --- a/src/Utilities/sparse_matrix.jl +++ b/src/Utilities/sparse_matrix.jl @@ -183,19 +183,21 @@ function Base.convert( ) end -function _first_in_column( - A::MutableSparseMatrixCSC{Tv,Ti}, - row::Integer, - col::Integer, -) where {Tv,Ti} +_indexing(A::MutableSparseMatrixCSC) = A.indexing +_indexing(::SparseArrays.SparseMatrixCSC) = OneBasedIndexing() + +const _SparseMatrixCSC{Tv,Ti} = + Union{MutableSparseMatrixCSC{Tv,Ti},SparseArrays.SparseMatrixCSC{Tv,Ti}} + +function _first_in_column(A::_SparseMatrixCSC, row::Integer, col::Integer) range = SparseArrays.nzrange(A, col) - row = _shift(row, OneBasedIndexing(), A.indexing) + row = _shift(row, OneBasedIndexing(), _indexing(A)) idx = searchsortedfirst(view(A.rowval, range), row) return get(range, idx, last(range) + 1) end function extract_function( - A::MutableSparseMatrixCSC{T}, + A::_SparseMatrixCSC{T}, row::Integer, constant::T, ) where {T} @@ -205,7 +207,7 @@ function extract_function( if idx > last(SparseArrays.nzrange(A, col)) continue end - r = _shift(A.rowval[idx], A.indexing, OneBasedIndexing()) + r = _shift(A.rowval[idx], _indexing(A), OneBasedIndexing()) if r == row push!( func.terms, @@ -217,7 +219,7 @@ function extract_function( end function extract_function( - A::MutableSparseMatrixCSC{T}, + A::_SparseMatrixCSC{T}, rows::UnitRange, constants::Vector{T}, ) where {T} @@ -231,7 +233,7 @@ function extract_function( if idx[col] > last(SparseArrays.nzrange(A, col)) continue end - row = _shift(A.rowval[idx[col]], A.indexing, OneBasedIndexing()) + row = _shift(A.rowval[idx[col]], _indexing(A), OneBasedIndexing()) if row != rows[output_index] continue end diff --git a/test/Utilities/matrix_of_constraints.jl b/test/Utilities/matrix_of_constraints.jl index 96069f4c63..fa2f405d17 100644 --- a/test/Utilities/matrix_of_constraints.jl +++ b/test/Utilities/matrix_of_constraints.jl @@ -714,6 +714,78 @@ function test_unsupported_constraint() return end +MOI.Utilities.@product_of_sets(_EqualTos, MOI.EqualTo{T},) + +function _equality_constraints( + A::AbstractMatrix{T}, + b::AbstractVector{T}, +) where {T} + sets = _EqualTos{T}() + for _ in eachindex(b) + MOI.Utilities.add_set( + sets, + MOI.Utilities.set_index(sets, MOI.EqualTo{T}), + ) + end + MOI.Utilities.final_touch(sets) + constants = MOI.Utilities.Hyperrectangle(b, b) + model = MOI.Utilities.MatrixOfConstraints{T}(A, constants, sets) + model.final_touch = true + return model +end + +# Inspired from MatrixOfConstraints +function test_lp_standard_form() + s = """ + variables: x1, x2 + cx1: x1 >= 0.0 + cx2: x2 >= 0.0 + c1: 1x1 == 5.0 + c2: 3x1 + 4x2 == 6.0 + minobjective: 7x1 + 8x2 + """ + expected = MOI.Utilities.Model{Float64}() + MOI.Utilities.loadfromstring!(expected, s) + + var_names = ["x1", "x2"] + con_names = ["c1", "c2"] + + A = SparseArrays.sparse([ + 1.0 0.0 + 3.0 4.0 + ]) + b = [5.0, 6.0] + form = MOI.Utilities.GenericModel{Float16}( + expected.objective, + expected.variables, + _equality_constraints(A, b), + ) + + model = MOI.Utilities.Model{Float64}() + MOI.copy_to(MOI.Bridges.Constraint.Scalarize{Float64}(model), form) + MOI.set( + model, + MOI.VariableName(), + MOI.VariableIndex.(eachindex(var_names)), + var_names, + ) + MOI.set( + model, + MOI.ConstraintName(), + MOI.ConstraintIndex{ + MOI.ScalarAffineFunction{Float64}, + MOI.EqualTo{Float64}, + }.( + eachindex(con_names), + ), + con_names, + ) + + MOI.Test.util_test_models_equal(model, expected, var_names, con_names) + + return +end + end TestMatrixOfConstraints.runtests()