Choosing between Symmetric and Hermitian programmatically in JuMP

I want to switch between Symmetric and Hermitian variables depending on the input data. For data I’d like to define a variable a, that can be either and then create the variable, e.g.

a = Symmetric
model = Model()
@variable(model, x[1:2,1:2], a)

but that errors. How do I pass them correctly?

Looking at the source code of the @variable macro, it seems like it wants to have Symmetric or Hermitian as a symbol, but

a = :Symmetric
model = Model()
@variable(model, x[1:2,1:2], a)

also fails. It does work if I do

a = :Symmetric
model = Model()
@eval @variable(model, x[1:2,1:2], $a)

but that’s a bit insane. What is going on here? What kind of argument does @variable want? Is it possible to do it without @eval?

1 Like

Try without the @eval: @variable(model, x[1:2,1:2], $a).

1 Like

That gives me an error:

ERROR: syntax: “$” expression outside quote

Why not just use Hermitian in all cases? For real data, Hermitian is functionally (and mathematically) equivalent to Symmetric. For complex data, Symmetric is of limited utility in linear algebra.

What I’m doing is switching between a variable that belongs to the d(d+1)/2 dimensional vector space of real Hermitian matrices, or one that belongs to the d^2 dimensional vector space of complex Hermitian matrices. “Symmetric” and “Hermitian” are just the names JuMP uses for these vector spaces.

@variable is a macro, so it operates before a=:Symmetric even takes place. I guess a simple if/else

if (...)
    @variable(model, x[...], Symmetric)
else
    @variable(model, x[...], Hermitian)
end

is your best option. Is there a tangible gain from passing a variable to the @variable macro?

Another possibility is to define

function add_variable(model, dim, ::Type{Symmetric})
    @variable(model, [1:dim, 1:dim], Symmetric)
end

at the cost of dealing with anonymous variables later on:

m[:x] = add_variable(m, 2, Symmetric)

I am using an if-else now, that’s what I want to avoid. It leads to a lot of copy-pasting, since I need to switch between real and complex variables in several places.

You can do:

julia> model = Model();

julia> a = SymmetricMatrixSpace()
SymmetricMatrixSpace()

julia> @variable(model, x[1:2,1:2], set=a)
2×2 LinearAlgebra.Symmetric{VariableRef, Matrix{VariableRef}}:
 x[1,1]  x[1,2]
 x[1,2]  x[2,2]

where it’s HermitianMatrixSpace() for Hermitian matrices. The docs mention set= in this section and SymmetricMatrixSpace here, but I don’t see anywhere they mention using it for programmatic construction, maybe that would be useful to add to the docs.

4 Likes

Thanks a lot, that works. Testing a little bit I found a slightly nicer syntax:

model = Model()
a = HermitianMatrixSpace()
@variable(model, x[1:2,1:2] in a)
2 Likes

I’ll update the docs: [docs] clarify how to pass PSD and Symmetric via set kwarg by odow · Pull Request #3940 · jump-dev/JuMP.jl · GitHub

2 Likes