diff --git a/src/Utilities/matrix_of_constraints.jl b/src/Utilities/matrix_of_constraints.jl index 921dd820ac..c6268ab9c4 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,18 @@ function rows end MOI.is_empty(v::MatrixOfConstraints) = MOI.is_empty(v.sets) -function MOI.empty!(v::MatrixOfConstraints{T}) where {T} - MOI.empty!(v.coefficients) - empty!(v.constants) - MOI.empty!(v.sets) +function _reset_caches!(v::MatrixOfConstraints{T}) where {T} 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)] + return +end + +function MOI.empty!(v::MatrixOfConstraints) + MOI.empty!(v.coefficients) + empty!(v.constants) + MOI.empty!(v.sets) + _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..c1ec5f177e 100644 --- a/src/Utilities/sparse_matrix.jl +++ b/src/Utilities/sparse_matrix.jl @@ -183,19 +183,23 @@ function Base.convert( ) end +_indexing(A::MutableSparseMatrixCSC) = A.indexing + +_indexing(::SparseArrays.SparseMatrixCSC) = OneBasedIndexing() + function _first_in_column( - A::MutableSparseMatrixCSC{Tv,Ti}, + A::Union{MutableSparseMatrixCSC,SparseArrays.SparseMatrixCSC}, row::Integer, col::Integer, -) where {Tv,Ti} +) 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::Union{MutableSparseMatrixCSC{T},SparseArrays.SparseMatrixCSC{T}}, row::Integer, constant::T, ) where {T} @@ -205,7 +209,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 +221,7 @@ function extract_function( end function extract_function( - A::MutableSparseMatrixCSC{T}, + A::Union{MutableSparseMatrixCSC{T},SparseArrays.SparseMatrixCSC{T}}, rows::UnitRange, constants::Vector{T}, ) where {T} @@ -231,7 +235,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..e6c5eec4a8 100644 --- a/test/Utilities/matrix_of_constraints.jl +++ b/test/Utilities/matrix_of_constraints.jl @@ -714,6 +714,57 @@ 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 + minobjective: 7x1 + 8x2 + cx1: x1 >= 0.0 + cx2: x2 >= 0.0 + c1: 1x1 == 5.0 + c2: 3x1 + 4x2 == 6.0 + """ + 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.(1:2), var_names) + F, S = MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64} + ci = MOI.ConstraintIndex{F,S}.(1:2) + MOI.set(model, MOI.ConstraintName(), ci, con_names) + MOI.Test.util_test_models_equal(model, expected, var_names, con_names) + return +end + end TestMatrixOfConstraints.runtests()