Skip to content

Commit 0b9a60a

Browse files
authored
[Utilities] Allow creating model from parts (#2902)
1 parent 100eab2 commit 0b9a60a

4 files changed

Lines changed: 94 additions & 19 deletions

File tree

src/Utilities/matrix_of_constraints.jl

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,24 @@ mutable struct MatrixOfConstraints{T,AT,BT,ST} <: MOI.ModelLike
9292
caches::Vector{Any}
9393
are_indices_mapped::Vector{BitSet}
9494
final_touch::Bool
95-
function MatrixOfConstraints{T,AT,BT,ST}() where {T,AT,BT,ST}
96-
model = new{T,AT,BT,ST}(AT(), BT(), ST(), Any[], BitSet[], false)
97-
MOI.empty!(model)
95+
function MatrixOfConstraints{T}(coefficients, constants, sets) where {T}
96+
model = new{T,typeof(coefficients),typeof(constants),typeof(sets)}(
97+
coefficients,
98+
constants,
99+
sets,
100+
Any[],
101+
BitSet[],
102+
false,
103+
)
104+
_reset_caches!(model)
98105
return model
99106
end
100107
end
101108

109+
function MatrixOfConstraints{T,AT,BT,ST}() where {T,AT,BT,ST}
110+
return MatrixOfConstraints{T}(AT(), BT(), ST())
111+
end
112+
102113
###
103114
### Interface for the .coefficients field
104115
###
@@ -292,13 +303,18 @@ function rows end
292303

293304
MOI.is_empty(v::MatrixOfConstraints) = MOI.is_empty(v.sets)
294305

295-
function MOI.empty!(v::MatrixOfConstraints{T}) where {T}
296-
MOI.empty!(v.coefficients)
297-
empty!(v.constants)
298-
MOI.empty!(v.sets)
306+
function _reset_caches!(v::MatrixOfConstraints{T}) where {T}
299307
v.caches =
300308
[Tuple{_affine_function_type(T, S),S}[] for S in set_types(v.sets)]
301309
v.are_indices_mapped = [BitSet() for _ in eachindex(v.caches)]
310+
return
311+
end
312+
313+
function MOI.empty!(v::MatrixOfConstraints)
314+
MOI.empty!(v.coefficients)
315+
empty!(v.constants)
316+
MOI.empty!(v.sets)
317+
_reset_caches!(v)
302318
v.final_touch = false
303319
return
304320
end

src/Utilities/model.jl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ mutable struct GenericModel{T,O,V,C} <: AbstractModelLike{T}
3232
# A useful dictionary for extensions to store things. These are
3333
# _not_ copied between models.
3434
ext::Dict{Symbol,Any}
35-
function GenericModel{T,O,V,C}() where {T,O,V,C}
36-
return new{T,O,V,C}(
35+
function GenericModel{T}(objective, variables, constraints) where {T}
36+
return new{T,typeof(objective),typeof(variables),typeof(constraints)}(
3737
"",
38-
O(),
39-
V(),
40-
C(),
38+
objective,
39+
variables,
40+
constraints,
4141
Dict{MOI.VariableIndex,String}(),
4242
nothing,
4343
Dict{MOI.ConstraintIndex,String}(),
@@ -47,6 +47,10 @@ mutable struct GenericModel{T,O,V,C} <: AbstractModelLike{T}
4747
end
4848
end
4949

50+
function GenericModel{T,O,V,C}() where {T,O,V,C}
51+
return GenericModel{T}(O(), V(), C())
52+
end
53+
5054
abstract type AbstractOptimizer{T} <: MOI.AbstractOptimizer end
5155

5256
"""

src/Utilities/sparse_matrix.jl

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,19 +183,23 @@ function Base.convert(
183183
)
184184
end
185185

186+
_indexing(A::MutableSparseMatrixCSC) = A.indexing
187+
188+
_indexing(::SparseArrays.SparseMatrixCSC) = OneBasedIndexing()
189+
186190
function _first_in_column(
187-
A::MutableSparseMatrixCSC{Tv,Ti},
191+
A::Union{MutableSparseMatrixCSC,SparseArrays.SparseMatrixCSC},
188192
row::Integer,
189193
col::Integer,
190-
) where {Tv,Ti}
194+
)
191195
range = SparseArrays.nzrange(A, col)
192-
row = _shift(row, OneBasedIndexing(), A.indexing)
196+
row = _shift(row, OneBasedIndexing(), _indexing(A))
193197
idx = searchsortedfirst(view(A.rowval, range), row)
194198
return get(range, idx, last(range) + 1)
195199
end
196200

197201
function extract_function(
198-
A::MutableSparseMatrixCSC{T},
202+
A::Union{MutableSparseMatrixCSC{T},SparseArrays.SparseMatrixCSC{T}},
199203
row::Integer,
200204
constant::T,
201205
) where {T}
@@ -205,7 +209,7 @@ function extract_function(
205209
if idx > last(SparseArrays.nzrange(A, col))
206210
continue
207211
end
208-
r = _shift(A.rowval[idx], A.indexing, OneBasedIndexing())
212+
r = _shift(A.rowval[idx], _indexing(A), OneBasedIndexing())
209213
if r == row
210214
push!(
211215
func.terms,
@@ -217,7 +221,7 @@ function extract_function(
217221
end
218222

219223
function extract_function(
220-
A::MutableSparseMatrixCSC{T},
224+
A::Union{MutableSparseMatrixCSC{T},SparseArrays.SparseMatrixCSC{T}},
221225
rows::UnitRange,
222226
constants::Vector{T},
223227
) where {T}
@@ -231,7 +235,7 @@ function extract_function(
231235
if idx[col] > last(SparseArrays.nzrange(A, col))
232236
continue
233237
end
234-
row = _shift(A.rowval[idx[col]], A.indexing, OneBasedIndexing())
238+
row = _shift(A.rowval[idx[col]], _indexing(A), OneBasedIndexing())
235239
if row != rows[output_index]
236240
continue
237241
end

test/Utilities/matrix_of_constraints.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,57 @@ function test_unsupported_constraint()
714714
return
715715
end
716716

717+
MOI.Utilities.@product_of_sets(_EqualTos, MOI.EqualTo{T},)
718+
719+
function _equality_constraints(
720+
A::AbstractMatrix{T},
721+
b::AbstractVector{T},
722+
) where {T}
723+
sets = _EqualTos{T}()
724+
for _ in eachindex(b)
725+
MOI.Utilities.add_set(
726+
sets,
727+
MOI.Utilities.set_index(sets, MOI.EqualTo{T}),
728+
)
729+
end
730+
MOI.Utilities.final_touch(sets)
731+
constants = MOI.Utilities.Hyperrectangle(b, b)
732+
model = MOI.Utilities.MatrixOfConstraints{T}(A, constants, sets)
733+
model.final_touch = true
734+
return model
735+
end
736+
737+
# Inspired from MatrixOfConstraints
738+
function test_lp_standard_form()
739+
s = """
740+
variables: x1, x2
741+
minobjective: 7x1 + 8x2
742+
cx1: x1 >= 0.0
743+
cx2: x2 >= 0.0
744+
c1: 1x1 == 5.0
745+
c2: 3x1 + 4x2 == 6.0
746+
"""
747+
expected = MOI.Utilities.Model{Float64}()
748+
MOI.Utilities.loadfromstring!(expected, s)
749+
var_names = ["x1", "x2"]
750+
con_names = ["c1", "c2"]
751+
A = SparseArrays.sparse([1.0 0.0; 3.0 4.0])
752+
b = [5.0, 6.0]
753+
form = MOI.Utilities.GenericModel{Float16}(
754+
expected.objective,
755+
expected.variables,
756+
_equality_constraints(A, b),
757+
)
758+
model = MOI.Utilities.Model{Float64}()
759+
MOI.copy_to(MOI.Bridges.Constraint.Scalarize{Float64}(model), form)
760+
MOI.set(model, MOI.VariableName(), MOI.VariableIndex.(1:2), var_names)
761+
F, S = MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}
762+
ci = MOI.ConstraintIndex{F,S}.(1:2)
763+
MOI.set(model, MOI.ConstraintName(), ci, con_names)
764+
MOI.Test.util_test_models_equal(model, expected, var_names, con_names)
765+
return
766+
end
767+
717768
end
718769

719770
TestMatrixOfConstraints.runtests()

0 commit comments

Comments
 (0)