@@ -665,6 +665,7 @@ function test_modify_scalar_coefficient_change()
665665 @test f ≈ 5 x + 3 y
666666 MOI. modify (model, c, MOI. ScalarCoefficientChange (y, 0 ))
667667 f = MOI. get (model, MOI. ConstraintFunction (), c)
668+ @test MOI. Utilities. is_canonical (f)
668669 @test f ≈ 5 x + 0 y
669670 return
670671end
@@ -826,6 +827,124 @@ function test_modify_set_constants()
826827 return
827828end
828829
830+ function test_modify_multirow_change ()
831+ model = _new_VectorSets ()
832+ src = MOI. Utilities. Model {Int} ()
833+ x = MOI. add_variables (src, 2 )
834+ terms = [
835+ MOI. VectorAffineTerm (1 , MOI. ScalarAffineTerm (2 , x[1 ])),
836+ MOI. VectorAffineTerm (1 , MOI. ScalarAffineTerm (3 , x[2 ])),
837+ MOI. VectorAffineTerm (2 , MOI. ScalarAffineTerm (4 , x[1 ])),
838+ MOI. VectorAffineTerm (2 , MOI. ScalarAffineTerm (5 , x[2 ])),
839+ ]
840+ func = MOI. VectorAffineFunction (terms, [0 , 0 ])
841+ c = MOI. add_constraint (src, func, MOI. Nonnegatives (2 ))
842+ index_map = MOI. copy_to (model, src)
843+ ci = index_map[c]
844+ f = MOI. get (model, MOI. ConstraintFunction (), ci)
845+ @test length (f. terms) == 4
846+ x1 = index_map[x[1 ]]
847+ x2 = index_map[x[2 ]]
848+ MOI. modify (model, ci, MOI. MultirowChange (x1, [(1 , 7 ), (2 , 8 )]))
849+ f = MOI. get (model, MOI. ConstraintFunction (), ci)
850+ coefs = Dict (
851+ (t. output_index, t. scalar_term. variable) =>
852+ t. scalar_term. coefficient for t in f. terms
853+ )
854+ @test coefs[(1 , x1)] == 7
855+ @test coefs[(2 , x1)] == 8
856+ @test coefs[(1 , x2)] == 3
857+ @test coefs[(2 , x2)] == 5
858+ return
859+ end
860+
861+ function test_modify_multirow_change_single_row ()
862+ model = _new_VectorSets ()
863+ src = MOI. Utilities. Model {Int} ()
864+ x = MOI. add_variables (src, 2 )
865+ terms = [
866+ MOI. VectorAffineTerm (1 , MOI. ScalarAffineTerm (2 , x[1 ])),
867+ MOI. VectorAffineTerm (1 , MOI. ScalarAffineTerm (3 , x[2 ])),
868+ MOI. VectorAffineTerm (2 , MOI. ScalarAffineTerm (4 , x[1 ])),
869+ MOI. VectorAffineTerm (2 , MOI. ScalarAffineTerm (5 , x[2 ])),
870+ ]
871+ func = MOI. VectorAffineFunction (terms, [0 , 0 ])
872+ c = MOI. add_constraint (src, func, MOI. Nonnegatives (2 ))
873+ index_map = MOI. copy_to (model, src)
874+ ci = index_map[c]
875+ x2 = index_map[x[2 ]]
876+ MOI. modify (model, ci, MOI. MultirowChange (x2, [(2 , 9 )]))
877+ f = MOI. get (model, MOI. ConstraintFunction (), ci)
878+ coefs = Dict (
879+ (t. output_index, t. scalar_term. variable) =>
880+ t. scalar_term. coefficient for t in f. terms
881+ )
882+ x1 = index_map[x[1 ]]
883+ @test coefs[(1 , x1)] == 2
884+ @test coefs[(2 , x1)] == 4
885+ @test coefs[(1 , x2)] == 3
886+ @test coefs[(2 , x2)] == 9
887+ return
888+ end
889+
890+ function test_modify_multirow_change_to_zero ()
891+ model = _new_VectorSets ()
892+ src = MOI. Utilities. Model {Int} ()
893+ x = MOI. add_variables (src, 2 )
894+ terms = [
895+ MOI. VectorAffineTerm (1 , MOI. ScalarAffineTerm (2 , x[1 ])),
896+ MOI. VectorAffineTerm (1 , MOI. ScalarAffineTerm (3 , x[2 ])),
897+ MOI. VectorAffineTerm (2 , MOI. ScalarAffineTerm (4 , x[1 ])),
898+ MOI. VectorAffineTerm (2 , MOI. ScalarAffineTerm (5 , x[2 ])),
899+ ]
900+ func = MOI. VectorAffineFunction (terms, [0 , 0 ])
901+ c = MOI. add_constraint (src, func, MOI. Nonnegatives (2 ))
902+ index_map = MOI. copy_to (model, src)
903+ ci = index_map[c]
904+ x1 = index_map[x[1 ]]
905+ x2 = index_map[x[2 ]]
906+ MOI. modify (model, ci, MOI. MultirowChange (x1, [(1 , 0 )]))
907+ f = MOI. get (model, MOI. ConstraintFunction (), ci)
908+ @test MOI. Utilities. is_canonical (f)
909+ @test length (f. terms) == 3
910+ coefs = Dict (
911+ (t. output_index, t. scalar_term. variable) =>
912+ t. scalar_term. coefficient for t in f. terms
913+ )
914+ @test ! haskey (coefs, (1 , x1))
915+ @test coefs[(2 , x1)] == 4
916+ @test coefs[(1 , x2)] == 3
917+ @test coefs[(2 , x2)] == 5
918+ return
919+ end
920+
921+ function test_modify_multirow_change_no_entry ()
922+ model = _new_VectorSets ()
923+ src = MOI. Utilities. Model {Int} ()
924+ x = MOI. add_variables (src, 3 )
925+ terms = [
926+ MOI. VectorAffineTerm (1 , MOI. ScalarAffineTerm (2 , x[1 ])),
927+ MOI. VectorAffineTerm (2 , MOI. ScalarAffineTerm (4 , x[1 ])),
928+ ]
929+ func = MOI. VectorAffineFunction (terms, [0 , 0 ])
930+ c = MOI. add_constraint (src, func, MOI. Nonnegatives (2 ))
931+ index_map = MOI. copy_to (model, src)
932+ ci = index_map[c]
933+ x3 = index_map[x[3 ]]
934+ MOI. modify (model, ci, MOI. MultirowChange (x3, [(1 , 0 )]))
935+ change = MOI. MultirowChange (x3, [(1 , 5 )])
936+ @test_throws (
937+ MOI. ModifyConstraintNotAllowed (
938+ ci,
939+ change,
940+ " cannot set a new non-zero coefficient because no entry " *
941+ " exists in the sparse matrix of `MatrixOfConstraints`" ,
942+ ),
943+ MOI. modify (model, ci, change),
944+ )
945+ return
946+ end
947+
829948function test_unsupported_constraint ()
830949 model = _new_ScalarSets ()
831950 x = MOI. VariableIndex (1 )
0 commit comments