Use add_to_expression!() for multi-dimension expressions

Hi all,
May I have your suggestions on how to use add_to_expression!() to add one multi-dimension expressions to another?
Say I have an expression A with dimension (n x m), and an expression B with dimension (n x m).
I received the following errors if I coded add_to_expression!(A, B):

ERROR: LoadError: MethodError: no method matching add_to_expression!(::Matrix{JuMP.AffExpr}, ::Matrix{JuMP.AffExpr})
Closest candidates are:
  add_to_expression!(::JuMP.GenericAffExpr{C, V}, ::V) where {C, V} at /Users/qingyuxu/.julia/packages/JuMP/zn6NT/src/aff_expr.jl:382
  add_to_expression!(::JuMP.GenericAffExpr{C, V}, ::V, ::Union{Number, LinearAlgebra.UniformScaling}) where {C, V} at /Users/qingyuxu/.julia/packages/JuMP/zn6NT/src/aff_expr.jl:408
  add_to_expression!(::JuMP.GenericQuadExpr{C, V}, ::Union{JuMP.GenericAffExpr{C, V}, V}, ::Union{Number, LinearAlgebra.UniformScaling}) where {C, V} at /Users/qingyuxu/.julia/packages/JuMP/zn6NT/src/quad_expr.jl:314
  ...

P.S. I used to do A +=B, but JuMP warns me ā€˜+=ā€™ is less efficient. Also, if A and B are vectors, add_to_expressions!() seems to work well.
P.S.2 I used += to get rid of the loops in the first place, so I am afraid looping through dimensions cannot help me here.

1 Like

Hi @xuqingyu,

Thereā€™s no explicit support for this, but you have a couple of options.

Assuming A and B have the same dimensions, then you can use broadcasting:

add_to_expression!.(A, B)

This will call add_to_expression! on each element pair of A and B.

Second, although I see your p.p.s., just use a loop! It wonā€™t be any slower.

Third, if your problem is small enough, just use +=. Itā€™s a warning, not an error. So if itā€™s fast enough for you, donā€™t worry about the ā€œbestā€ thing to do.

You might also be able to restructure your code to avoid constructing expressions outside the macros. Hereā€™s an explanation in the docs for why we warn that += is less efficient: Performance tips Ā· JuMP

Thanks @odow, this is just wonderful! I love the idea of broadcasting!
For loop not being slower, is the reason because broadcasting or += will undergo looping anyway?
(the main reason I want to use += or add_to_expressions!, but not loop, is that I want to reduce the length of the code)

Thank you again!

Something like:

for (a, b) in zip(A, B)
    add_to_expression!(a, b)
end

is essentially equivalent to

add_to_expression!.(A, B)

There should be minimal performance differences.

I love the idea of broadcasting!

And yes! Quite powerful and applicable to any Julia function, not just JuMP. This is one issue we struggle with in terms of documentation. We donā€™t provide explicit syntax in JuMP for a lot of operations because you can compose other Julia operators to the same effect.