Skip to content

Commit fe25f39

Browse files
committed
Implement scalar coefficient change for MatrixOfConstraints
1 parent def69e1 commit fe25f39

3 files changed

Lines changed: 155 additions & 0 deletions

File tree

src/Utilities/matrix_of_constraints.jl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,22 @@ and [`MOI.VectorConstantChange`](@ref) for [`MatrixOfConstraints`](@ref).
246246
"""
247247
function modify_constants end
248248

249+
"""
250+
modify_coefficients(
251+
coefficients,
252+
row::Integer,
253+
col::Integer,
254+
new_coefficient,
255+
)
256+
257+
Modify `coefficients` in-place to store `new_coefficient` at position
258+
`(row, col)`.
259+
260+
This function must be implemented to enable
261+
[`MOI.ScalarCoefficientChange`](@ref) for [`MatrixOfConstraints`](@ref).
262+
"""
263+
function modify_coefficients end
264+
249265
###
250266
### Interface for the .sets field
251267
###
@@ -698,6 +714,24 @@ function MOI.modify(
698714
return
699715
end
700716

717+
function MOI.modify(
718+
model::MatrixOfConstraints,
719+
ci::MOI.ConstraintIndex,
720+
change::MOI.ScalarCoefficientChange,
721+
)
722+
try
723+
modify_coefficients(
724+
model.coefficients,
725+
rows(model, ci),
726+
change.variable.value,
727+
change.new_coefficient,
728+
)
729+
catch
730+
throw(MOI.ModifyConstraintNotAllowed(ci, change))
731+
end
732+
return
733+
end
734+
701735
function modify_constants(
702736
b::AbstractVector{T},
703737
row::Integer,

src/Utilities/sparse_matrix.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,27 @@ function load_terms(
161161
return
162162
end
163163

164+
function modify_coefficients(
165+
A::Union{MutableSparseMatrixCSC{Tv},SparseArrays.SparseMatrixCSC{Tv}},
166+
row::Integer,
167+
col::Integer,
168+
new_coefficient::Tv,
169+
) where {Tv}
170+
idx = _first_in_column(A, row, col)
171+
range = SparseArrays.nzrange(A, col)
172+
r = _shift(row, OneBasedIndexing(), _indexing(A))
173+
if idx <= last(range) && A.rowval[idx] == r
174+
A.nzval[idx] = new_coefficient
175+
elseif !iszero(new_coefficient)
176+
error(
177+
"Cannot set a new non-zero coefficient at ($row, $col) because " *
178+
"no entry exists in the sparse matrix. Adding new entries to a " *
179+
"`MutableSparseMatrixCSC` after `final_touch` is not supported.",
180+
)
181+
end
182+
return
183+
end
184+
164185
"""
165186
Base.convert(
166187
::Type{SparseMatrixCSC{Tv,Ti}},

test/Utilities/test_matrix_of_constraints.jl

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,106 @@ function test_set_types_fallback()
636636
return
637637
end
638638

639+
function test_modify_scalar_coefficient_change()
640+
model = MOI.Utilities.GenericOptimizer{
641+
Int,
642+
MOI.Utilities.ObjectiveContainer{Int},
643+
MOI.Utilities.VariablesContainer{Int},
644+
MOI.Utilities.MatrixOfConstraints{
645+
Int,
646+
MOI.Utilities.MutableSparseMatrixCSC{
647+
Int,
648+
Int,
649+
MOI.Utilities.OneBasedIndexing,
650+
},
651+
MOI.Utilities.Hyperrectangle{Int},
652+
ScalarSets{Int},
653+
},
654+
}()
655+
x = MOI.add_variable(model)
656+
y = MOI.add_variable(model)
657+
func = 2x + 3y
658+
set = MOI.EqualTo(1)
659+
c = MOI.add_constraint(model, func, set)
660+
MOI.Utilities.final_touch(model, nothing)
661+
f = MOI.get(model, MOI.ConstraintFunction(), c)
662+
@test f 2x + 3y
663+
MOI.modify(model, c, MOI.ScalarCoefficientChange(x, 5))
664+
f = MOI.get(model, MOI.ConstraintFunction(), c)
665+
@test f 5x + 3y
666+
MOI.modify(model, c, MOI.ScalarCoefficientChange(y, 0))
667+
f = MOI.get(model, MOI.ConstraintFunction(), c)
668+
@test f 5x + 0y
669+
return
670+
end
671+
672+
function test_modify_scalar_coefficient_change_zero_based()
673+
model = MOI.Utilities.GenericOptimizer{
674+
Float64,
675+
MOI.Utilities.ObjectiveContainer{Float64},
676+
MOI.Utilities.VariablesContainer{Float64},
677+
MOI.Utilities.MatrixOfConstraints{
678+
Float64,
679+
MOI.Utilities.MutableSparseMatrixCSC{
680+
Float64,
681+
Int,
682+
MOI.Utilities.ZeroBasedIndexing,
683+
},
684+
MOI.Utilities.Hyperrectangle{Float64},
685+
ScalarSets{Float64},
686+
},
687+
}()
688+
src = MOI.Utilities.Model{Float64}()
689+
MOI.Utilities.loadfromstring!(
690+
src,
691+
"""
692+
variables: x, y
693+
minobjective: x + y
694+
c: x + 2.0 * y <= 3.0
695+
""",
696+
)
697+
index_map = MOI.copy_to(model, src)
698+
c = MOI.get(model, MOI.ConstraintIndex, "c")
699+
x = index_map[MOI.get(src, MOI.VariableIndex, "x")]
700+
y = index_map[MOI.get(src, MOI.VariableIndex, "y")]
701+
f = MOI.get(model, MOI.ConstraintFunction(), c)
702+
@test f 1.0x + 2.0y
703+
MOI.modify(model, c, MOI.ScalarCoefficientChange(x, 4.0))
704+
f = MOI.get(model, MOI.ConstraintFunction(), c)
705+
@test f 4.0x + 2.0y
706+
return
707+
end
708+
709+
function test_modify_scalar_coefficient_change_no_entry()
710+
model = MOI.Utilities.GenericOptimizer{
711+
Int,
712+
MOI.Utilities.ObjectiveContainer{Int},
713+
MOI.Utilities.VariablesContainer{Int},
714+
MOI.Utilities.MatrixOfConstraints{
715+
Int,
716+
MOI.Utilities.MutableSparseMatrixCSC{
717+
Int,
718+
Int,
719+
MOI.Utilities.OneBasedIndexing,
720+
},
721+
MOI.Utilities.Hyperrectangle{Int},
722+
ScalarSets{Int},
723+
},
724+
}()
725+
x = MOI.add_variable(model)
726+
y = MOI.add_variable(model)
727+
func = 2x
728+
set = MOI.EqualTo(1)
729+
c = MOI.add_constraint(model, func, set)
730+
MOI.Utilities.final_touch(model, nothing)
731+
MOI.modify(model, c, MOI.ScalarCoefficientChange(y, 0))
732+
@test_throws(
733+
MOI.ModifyConstraintNotAllowed,
734+
MOI.modify(model, c, MOI.ScalarCoefficientChange(y, 3)),
735+
)
736+
return
737+
end
738+
639739
function test_modify_vectorsets()
640740
model = _new_VectorSets()
641741
src = MOI.Utilities.Model{Int}()

0 commit comments

Comments
 (0)