JuMP MethodError: Cannot `convert` an object of type AffExpr to an object of type VariableRef

The first way of imposing the constraints works. The second way causes the error below. Is there a better way to impose theses constraints?

using JuMP
using LinearAlgebra
v = 64
J = 10
model = Model()

@variable(model, -(v-1) <= Q[1:J,1:J] <= v-1, Int)
@variable(model, 0 <= p[1:J] <= v-1, Int)
# Define p via equality constraints based on manipulations of other variables.
@constraint(model, Q .== repeat(p', outer = [J,1])-repeat(p, outer = [1,J]))
@variable(model, 0 <= uQ[1:J,1:J] <= v-1, Int)
  1. @constraint(model, uQ .== triu(Q+zeros(J,J),1))
  2. @constraint(model, uQ .== triu(Q,1))

Error caused by method 2:

ERROR: MethodError: Cannot `convert` an object of type AffExpr to an object of type VariableRef
Closest candidates are:
  convert(::Type{T}, ::T) where T at ~/julia-1.7.3/share/julia/base/essentials.jl:218
  VariableRef(::Any, ::Any) at ~/.julia/packages/JuMP/vuP7I/src/variables.jl:203
 [1] setindex!
   @ ./array.jl:905 [inlined]
 [2] triu!
   @ ~/julia-1.7.3/share/julia/stdlib/v1.7/LinearAlgebra/src/dense.jl:139 [inlined]
 [3] triu(M::Matrix{VariableRef}, k::Int64)
   @ LinearAlgebra ~/julia-1.7.3/share/julia/stdlib/v1.7/LinearAlgebra/src/dense.jl:145
 [4] macro expansion
   @ ~/.julia/packages/MutableArithmetics/9dpep/src/rewrite.jl:322 [inlined]
 [5] macro expansion
   @ ~/.julia/packages/JuMP/vuP7I/src/macros.jl:831 [inlined]
 [6] top-level scope
   @ REPL[20]:1

For the first part, I would write it as

using JuMP
import LinearAlgebra
J, v = 3, 64
model = Model()
@variable(model, -(v-1) <= Q[1:J, 1:J] <= v-1, Int)
@variable(model, 0 <= p[1:J] <= v-1, Int)
@constraint(model, [r=1:J, c=1:J], Q[r, c] == p[c] - p[r])

If you’re coming from Matlab, it takes a bit of getting used to being able to write things other than vector operations, but I think the explicit Q[r, c] == p[c] - p[r] is clearer than the use of repeat with outer, etc.

For the second part, this is a long-standing issue at the interface between JuMP and LinearAlgebra. Essentially, if you have a number x::T, then LinearAlgebra expects zero(T) to also be the same type T. Unfortunately, zero(VariableRef) is an AffExpr which breaks this assumption and leads to a lot of trouble when people try to construct LinearAlgebra matrix types with JuMP variables. As you found, the solution is to convert it to an AffExpr first, either by adding 0.0, or by multiplying by 1.0.

@variable(model, 0 <= uQ[1:J,1:J] <= v-1, Int)
@constraint(model, uQ .== LinearAlgebra.triu(Q .+ 0.0, 1))

You could also do something like

@variable(model, 0 <= uQ[1:J,1:J] <= v-1, Int)
for r in 1:J, c in 1:J
    if c > r
        @constraint(model, uQ[r, c] == Q[r, c])
        fix(uQ[r, c], 0; force = true)

In your example, an alternative way to realize the constraint:
@constraint(model, [r=1:J, c=1:J], Q[r, c] == p[c] - p[r])
is the vectorized constraint:
@constraint(model, Q .== p’ .- p)