import JuMP, Gurobi, LinearAlgebra
model = JuMP.Model(Gurobi.Optimizer)
JuMP.@variable(model, X[1:2, 1:2] >= 0, Symmetric)
JuMP.optimize!(model)
Xt = JuMP.value.(X) # ⚠️ Can this behavior be improved?
# julia> typeof(X)
# LinearAlgebra.Symmetric{JuMP.VariableRef, Matrix{JuMP.VariableRef}}
# julia> typeof(Xt)
# Matrix{Float64} (alias for Array{Float64, 2})
Xt = LinearAlgebra.Symmetric(Xt) # Since JuMP do not support, I have to do this myself
Here is another example
import JuMP, Gurobi, LinearAlgebra
model = JuMP.Model(Gurobi.Optimizer)
JuMP.@variable(model, x); JuMP.@variable(model, y)
trivial_value = 999
JuMP.@expression(model, X, LinearAlgebra.Symmetric([0 x; trivial_value y]))
JuMP.optimize!(model)
Xt = JuMP.value.(X) # ⚠️ Can this behavior be improved?
julia> typeof(X)
LinearAlgebra.Symmetric{JuMP.AffExpr, Matrix{JuMP.AffExpr}}
julia> typeof(Xt)
Matrix{Float64} (alias for Array{Float64, 2})
These two probably need to stay the way they are. The product of two symmetric matrices is not necessarily symmetric. In your specific examples, the result is symmetric because the factors are identical, but that’s a property of the values, not their types. It’s generally desirable for the return type to only depend on the argument types, not the argument values.
For S.^2 it would be reasonable to return Symmetric, but changing that now might be considered too breaking, that is, not backward compatible.
I think it’s fine to change return types across versions, given that types are tightly coupled with performance. This is somewhat breaking if people are explicitly dispatching on a Matrix, but the return type is not documented to be stable across versions.