LinearAlgebra: change in behavior between 1.1 and 1.7

The following code gives different results under 1.1 and 1.7.

PP =      [1  0  1  0  1  0  
           0  1  1  0  1  0  
           1  0  1  0  0  1  
           1  0  0  1  0  1  
           0  1  0  1  1  0  
           0  1  0  1  0  1] // 3  

using LinearAlgebra
eigvals(PP)

julia 1.1 returns a 6-element Array{Float64,1}
julia 1.7 returns a 6-element Vector{ComplexF64}

Why is it so? I spotted this change because 1.7 gives me an error further down the computation which was not there with 1.1, coming from taking the maximum of a complex-valued matrix.

maximum(eigvals(PP))  # 1.7
no method matching isless(::ComplexF64, ::ComplexF64)

I cannot reproduce on a freshly built (0 days old master) 1.7 on macOS

julia> PP =      [1  0  1  0  1  0
                  0  1  1  0  1  0
                  1  0  1  0  0  1
                  1  0  0  1  0  1
                  0  1  0  1  1  0
                  0  1  0  1  0  1] // 3
6×6 Matrix{Rational{Int64}}:
 1//3  0//1  1//3  0//1  1//3  0//1
 0//1  1//3  1//3  0//1  1//3  0//1
 1//3  0//1  1//3  0//1  0//1  1//3
 1//3  0//1  0//1  1//3  0//1  1//3
 0//1  1//3  0//1  1//3  1//3  0//1
 0//1  1//3  0//1  1//3  0//1  1//3

julia> using LinearAlgebra

julia> eigvals(PP)
6-element Vector{Float64}:
 -1.1102230246251565e-16
  8.326672684688674e-17
  0.33333333333333326
  0.33333333333333326
  0.33333333333333337
  1.0

julia> versioninfo()
Julia Version 1.7.0-DEV.896
Commit 2f0156a6c7* (2021-04-09 22:26 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin20.3.0)
  CPU: Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, skylake)
Environment:
  JULIA_EDITOR = /Applications/Emacs.app/Contents/MacOS/Emacs "$@"
1 Like

Not 100% sure, but I think this change (returning Complex{T} eigvals for a Matrix{T<:Real}) would have been made to improve type-stability of eigvals. Matrices with all-real entries can have complex eigenvalues (in conjugate pairs). The only way the compiler can safely guess the eigvals output type is if complex eigenvalue arrays are always returned, even if all eigenvalues have zero imaginary part. Julia code is much faster when the compiler can infer the type of data returned by a function, so I would bet this change improved the performance of eigvals in some cases.

Weird. My installation is from yesterday. I am using linux, julia was compiled from source.

              _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.7.0-DEV.894 (2021-04-09)
 _/ |\__'_|_|_|\__'_|  |  Commit 4996445df3* (0 days old master)
|__/                   |

julia> PP =      [1  0  1  0  1  0
                         0  1  1  0  1  0
                         1  0  1  0  0  1
                         1  0  0  1  0  1
                         0  1  0  1  1  0
                         0  1  0  1  0  1] // 3
6×6 Matrix{Rational{Int64}}:
 1//3  0//1  1//3  0//1  1//3  0//1
 0//1  1//3  1//3  0//1  1//3  0//1
 1//3  0//1  1//3  0//1  0//1  1//3
 1//3  0//1  0//1  1//3  0//1  1//3
 0//1  1//3  0//1  1//3  1//3  0//1
 0//1  1//3  0//1  1//3  0//1  1//3

julia> using LinearAlgebra

julia> eigvals(PP)
6-element Vector{ComplexF64}:
 1.3158237332208615e-16 - 8.012357677697767e-17im
 1.3158237332208615e-16 + 8.012357677697767e-17im
      0.333333333333333 + 0.0im
    0.33333333333333315 + 0.0im
     0.3333333333333333 + 0.0im
     0.9999999999999996 + 0.0im