JuMP warning messages

I get this warning message on some optimization problems.

┌ Warning: The addition operator has been used on JuMP expressions a large number of times. This warning is safe to ignore but may indicate that model generation is slower than necessary. For performance reasons, you should not add expressions in a loop. Instead of x += y, use add_to_expression!(x,y) to modify x in place. If y is a single variable, you may also use add_to_expression!(x, coef, y) for x += coef*y.
└ @ JuMP C:\Users\a\.julia\packages\JuMP\MsUSY\src\JuMP.jl:747

I only get this when I am reading large amounts of data from a spreadsheet for the optimisation problem, and not on simple problems. I suspect the source is the constraint calculation. I have given a MWE below, which does not yield the same warning but it should give an idea about how the constraint is determined. How can add_to_expression!(x,y) be used here to make the code more efficient?

using JuMP
using Clp
myModel = Model(with_optimizer(Clp.Optimizer))
prices = [99.74, 91.22, 98.71, 103.75, 97.15]
cashFlows = [4 5 2.5 5 4; 4 5 2.5 5 4; 4 5 2.5 5 4; 4 5 2.5 5 4; 4 5 102.5 5 4;4 5 0 105 104;4 105 0 0 0; 104 0 0 0 0]
Liab_CFs = [5, 7, 7, 6, 8, 7, 20, 0]*1000
Rates = [0.01, 0.015, 0.017, 0.019, 0.02, 0.025, 0.027, 0.029]
Disc= [0.99009901, 0.970661749, 0.950686094, 0.927477246, 0.90573081, 0.862296866, 0.829863942, 0.795567442]'
nBonds = [10, 100, 20, 30, 5]*1000
@variable(myModel, 0<= x[b = 1:length(nBonds)] <= nBonds[b])
@objective(myModel,Min,prices' *x) 

A=Liab_CFs-cashFlows*x
M=[AffExpr(0.0) for k = 1:8]
M[1] = A[1]
M2[1] = M[1]
for k = 2:length(A)
        M[k] = (A[k]*Disc[k]+M[k-1])
        M2[k] = M[k]/Disc[k]
end
@constraint(myModel, constraint1,M2 .<=0.05*53.844)
optimize!(myModel)

In general, all algebra with JuMP expressions should take place inside JuMP macros, or use add_to_expression!.

Thus, A * x + b is slow, but @expression(model, A * x + b) is fast.

These are the lines that are causing issues:

A=Liab_CFs-cashFlows*x
M=[AffExpr(0.0) for k = 1:8]
M[1] = A[1]
M2[1] = M[1]
for k = 2:length(A)
        M[k] = (A[k]*Disc[k]+M[k-1])
        M2[k] = M[k]/Disc[k]
end

You need, for example

A = @expression(model, Liab_CFs - cashFlows * x)
M=[AffExpr(0.0) for k = 1:8]
M[1] = A[1]
# M2[1] = M[1]
for k = 2:length(A)
    add_to_expression!(M[k], A[k] * Disc[k])
    add_to_expression!(M[k], M[k-1])
    # M2[k] = M[k]/Disc[k]
end

I’ve commented out M2 because I don’t know what that is.

M2 is an affine expression created by element wise division of M and Disc . To eliminate it from the loop, I will change the constraint to M./Disc .<=0.05*53.844 from M2 .<=0.05*53.844 if it cannot be included inside the loop.

A= @expression(myModel, Liab_CFs - cashFlows * x)
M=[AffExpr(0.0) for k = 1:8]
M[1] = A[1]
for k = 2:length(A)
    add_to_expression!(M[k], A[k] * Disc[k])
    add_to_expression!(M[k], M[k-1])
end
@constraint(myModel, M./Disc .<=0.05*53.844)

The issue is the the first commented line M2[1] = M[1] errors because M2 is not defined.

You can go:

add_to_expression!(M2[k], 1/Disc[k], M[k])

But

@constraint(myModel, [k = 1:K], M[k] / Disc[k] <= 0.05 * 53.844)

is probably a nicer way to write it.

Thanks. I forgot to include the code: M2=[AffExpr(0.0) for k = 1:8]

Is there much difference in terms of performance between the following two constraint definitions? I used benchmark tools but each time I run the script I get a different answer.

@constraint(myModel, [k = 1:8], M[k] / Disc[k] <= 0.05 * 53.844)
or
@constraint(myModel, M ./ Disc .<= 0.05 * 53.844)

They are identical. It is a matter of personal preference.