How to extend the JuMP @variable so that it can construct a sparse array which can be used as a full matrix?

Hi all

In fact, this question is a derivative of my last question.

I want to create a 4x4 array variables, but it only has a value at the index (1, 2), (2, 1), (2, 3), (3, 2) and the rest is 0.

@blegat provide several solutions, one of which best meets my expectations, as follows:

model = Model()
x = zeros(AffExpr, 3, 3)
x[1, 1] = @variable(model, base_name = "x[1, 1]")
x[2, 2] = @variable(model, base_name = "x[2, 2]")
x[1, 3] = @variable(model, base_name = "x[1, 3]")
x[3, 1] = @variable(model, base_name = "x[3, 1]")
x[3, 3] = @variable(model, base_name = "x[3, 3]")

julia> x
3×3 Matrix{AffExpr}:
 x[1, 1]  0        x[1, 3]
 0        x[2, 2]  0
 x[3, 1]  0        x[3, 3]

After my simple test, the “sparse” array variable x can be used as a “full” matrix.

For example, the most concerned am I is matrix multiplication and indexing:

julia> rand(3, 3) * x
3×3 Matrix{AffExpr}:
 0.39383698459321326 x[1, 1] + 0.48046499431872314 x[3, 1]  0.1500480109188398 x[2, 2]  0.39383698459321326 x[1, 3] + 0.48046499431872314 x[3, 3]
 0.9024020538088555 x[1, 1] + 0.21740175234965387 x[3, 1]   0.7251245455266802 x[2, 2]  0.9024020538088555 x[1, 3] + 0.21740175234965387 x[3, 3]
 0.47900776569507086 x[1, 1] + 0.9605059975897137 x[3, 1]   0.832551981599313 x[2, 2]   0.47900776569507086 x[1, 3] + 0.9605059975897137 x[3, 3]

julia> x[1, 1] + x[1, 2]
x[1, 1]

Thank you very much for the help provided by @blegat

Now, what I want to do is to make these codes more generic. I think I can extend @variable

In the following link, there is a very simple example to teach me how to extend @variable
https://jump.dev/JuMP.jl/v0.21.8/developers/extensions/

Maybe it’s because my level is too low. I cannot follow it very well. (To be honest, I am confused about it.)

So, I want to study exactly how @variable works, but I am prevented from accessing both JuMP.add_variable and JuMP.build_variable functions because of Macros.

For example, when I create a JuMP variable

@variable(model, x[1:3, 1:3])

How does JuMP identify x as a “name”, and identify [1:3, 1:3] as a “dimension”, …?

What information should I refer to, in order to achieve my idea? like the form shown as follows:

@variable(model, x[1:3, 1:3], Sparse, row=[1, 2, 2, 3], col=[2, 1, 3, 2])

I hope you won’t get bored with my long descriptions

Thanks a lot!

1 Like

I’ve recently improved this in the dev documentation: Variables · JuMP.

Namely: don’t extend JuMP, just use regular Julia data structures:

using JuMP, SparseArrays
model = Model()
x = @variable(model, [1:4])

julia> X = sparse([1, 2, 2, 3], [2, 1, 3, 2], x)
3×3 SparseMatrixCSC{VariableRef, Int64} with 4 stored entries:
 ⋅       noname  ⋅
 noname  ⋅       noname
 ⋅       noname  ⋅

julia> b = [1, 2, 3];

julia> X * b
3-element Vector{AffExpr}:
 2 noname
 noname + 3 noname
 2 noname

Now you might run into some weirdness, because of how zero(VariableRef) works (it is not the type VariableRef!).

So things like

julia> X .* X
3×3 SparseMatrixCSC{QuadExpr, Int64} with 9 stored entries:
 0        noname²  0
 noname²  0        noname²
 0        noname²  0

Will become dense.