Problems with Diagonal from LinearAlgebra

I have been using the Diagonal function to take a vector array and produce a matrix with entries of the vector along the diagonal. It used to work fine but now instead of producing a matrix with entries along the diagonal, it now just makes a 1x1 scalar.

using LinearAlgebra

myvec = [1 2 3]
mymat = Diagonal(myvec)

# previously produced 
mymat = [1 0 0; 0 2 0; 0 0 3]

#now produces 
mymat = 1

I think it has something to do with types? For instance, when it works it is a 3x3 Diagonal{type, Array{type, 1}}, and when it doesn’t it’s a 1x1 Diagoanl{similar}. The weird thing is that I didn’t change my code from last week to this, and it used to work, but now it does not. Any ideas would be most helpful.

1 Like

Also even when I try something like

using LinearAlgebra

myvec = [1 2 3]
mymat = Diagonal(myvec')

I sometimes get the same problem, and sometimes I don’t.

I think there may be some state in your session that is not part of your minimal working example, which causes the seemingly different behavior (eg you are overwriting some variables). This should of course be deterministic, and the expected behavior is

julia> using LinearAlgebra

julia> Diagonal(1:3)
3Ă—3 Diagonal{Int64,UnitRange{Int64}}:
 1  â‹…  â‹…
 â‹…  2  â‹…
 â‹…  â‹…  3

julia> Diagonal(ones(3))
3Ă—3 Diagonal{Float64,Array{Float64,1}}:
 1.0   â‹…    â‹… 
  â‹…   1.0   â‹… 
  â‹…    â‹…   1.0

as documented (?Diagonal).

Agreed, but if all I’m doing is overwriting some variables, why does that change things? And how can I prevent that from happening?

The problem arises from a 1x3 Array{Int64, 2} vector I’ve defined in my code analogously to my toy example. Furthermore it is defined within a function, which I believe means that it is defined each time I run that function (so I can’t be overwriting it in the REPL). I’ve also tried defining it as a 3x1 Array{Int64,2} to no success. I haven’t updated Julia to the most recent version or anything. I’m still using 1.1.1

For your intended behavior, you should provide a vector (Array{Int64, 1}), not a matrix (Array{Int64, 2}). Diagonal has different methods for these two different types.

help?> Diagonal
search: Diagonal Bidiagonal Tridiagonal SymTridiagonal
  Diagonal(A::AbstractMatrix)
  Construct a matrix from the diagonal of A.
  Examples
  ≡≡≡≡≡≡≡≡≡≡
  julia> A = [1 2 3; 4 5 6; 7 8 9]
  3Ă—3 Array{Int64,2}:
   1  2  3
   4  5  6
   7  8  9
  julia> Diagonal(A)
  3Ă—3 Diagonal{Int64,Array{Int64,1}}:
   1  â‹…  â‹…
   â‹…  5  â‹…
   â‹…  â‹…  9
-------------------------------------------------------------------------
  Diagonal(V::AbstractVector)
  Construct a matrix with V as its diagonal.
  Examples
  ≡≡≡≡≡≡≡≡≡≡
  julia> V = [1, 2]
  2-element Array{Int64,1}:
   1
   2
  julia> Diagonal(V)
  2Ă—2 Diagonal{Int64,Array{Int64,1}}:
   1  â‹…
   â‹…  2
1 Like

By changing the inputs, the functions give different results.

It is hard to answer this without concrete code.

It would be much easier to help you if you provided the a self-contained example that demonstrates the problem in a freshly started Julia session.

2 Likes

I guess what I would like to highlight is there seem to be some simple errors in how Diagonal is defined. Or perhaps I just don’t understand the usefulness of how it is currently defined. Why does

x = [1 2 3]
different_x = [1, 2, 3]

yield different results when calling


Diagonal(x)

Diagonal(different_x)
1 Like

Also you misunderstand my statement. I can re-write it back to the original but it still behaves strangely, and I don’t understand why that is.

Because the former is a matrix and the latter a vector (you should in general avoid using 1x3 matrices as vector, unfortunately, a lot of people are doing it).

If you input a matrix to the Diagonal constructor it will just use its diagonal, which for a 1x3 matrix is only a single element.

1 Like

The key is that [1 2 3] creates a one row matrix, not a vector. Calling Diagonal(::Matrix) extracts the diagonal from the matrix and constructs a new matrix with it. The reason Diagonal(myvec') sometimes works and sometimes doesn’t has to do with where myvec came from.

The sure-fire way to get the behavior you want is to use Diagonal(vec(x)), which will flatten x into a vector and ensure it gets placed on the diagonal.

3 Likes

In addition to what @DNF and @mbauman explained, you can use the introspection/reflection facilities of Julia to investigate problems like this next time you encounter something unexpected. Eg try

@which Diagonal([1 2 3])
@which Diagonal([1, 2, 3])

and also @edit instead of @which which should take you to the relevant method.

3 Likes

…also just typing in an expression and looking what the REPL tells is quite helpful. Like trying out a few construction ways to create arrays/vectors/matrices:

julia> [1 2 3]
1Ă—3 Array{Int64,2}:
 1  2  3

julia> [1, 2, 3]
3-element Array{Int64,1}:
 1
 2
 3

julia> [1; 2; 3]
3-element Array{Int64,1}:
 1
 2
 3

julia> [[1] [2] [3]]
1Ă—3 Array{Int64,2}:
 1  2  3

julia> [[1], [2], [3]]
3-element Array{Array{Int64,1},1}:
 [1]
 [2]
 [3]
2 Likes