Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Diagonal(::AbstractMatrix) behavior differs from documentation in certain cases #28581

Closed
KlausC opened this issue Aug 11, 2018 · 3 comments
Closed
Labels
docs This change adds or pertains to documentation

Comments

@KlausC
Copy link
Contributor

KlausC commented Aug 11, 2018

  | | |_| | | | (_| |  |  Version 1.1.0-DEV.10 (2018-08-11)
 _/ |\__'_|_|_|\__'_|  |  sparsearrays-triangular-operations/72f777ee96 (fork: 8 commits, 0 days)
|__/                   |

Whereas docu states:

help?> Diagonal
  Diagonal(A::AbstractMatrix)
  Construct a matrix from the diagonal of A.

When applied to a subtype of AbstractMatrix we obtain the following results:

julia> A = UpperTriangular(sparse([1.0 0;20 0]))
2×2 UpperTriangular{Float64,SparseMatrixCSC{Float64,Int64}}:
 1.0  0.0
  ⋅   0.0

julia> Diagonal(A)
2×2 Diagonal{Float64,SparseVector{Float64,Int64}}:
 1.0   ⋅ 
  ⋅   0.0

julia> A[1,2] = 1
1

julia> Diagonal(A)
ERROR: ArgumentError: matrix cannot be represented as Diagonal
Stacktrace:
 [1] Diagonal(::UpperTriangular{Float64,SparseMatrixCSC{Float64,Int64}}) at /home/julia/julia/usr/share/julia/stdlib/v1.1/LinearAlgebra/src/special.jl:48
 [2] top-level scope at none:0

That means, in this case the Diagonal is not constructed from the diagonal of A, but an error is thrown, if isdiag(A) == false.
The same behavior applies for some other subtypes of AbstractMatrix, and not for others.
IMO the check for diagonality should be removed from the constructors, and in all cases
Diagonal(A::AbstractMatrix) = Diagonal(diag(A)) should be true.
Alternatively the docu string should warn the user about the error check in some and which cases.

@kshyatt kshyatt added the docs This change adds or pertains to documentation label Aug 14, 2018
@chriscoey
Copy link

chriscoey commented Aug 24, 2018

IMO the check for diagonality should be removed from the constructors

Makes sense. Can this be fixed in 1.x?

@KlausC
Copy link
Contributor Author

KlausC commented Sep 23, 2018

I think changing the doc is not a good solution, because that would complicate a simple situation and manifest the inconsistent behaviour.
Why should Diagonal(ones(2,2)) be allowed, but not Diagonal(UpperTriangular(ones(2,2))? Logically both are identical.

Looking at Bidiagonal and Tridiagonal gives a similar picture:

julia> Bidiagonal(UpperTriangular(ones(3,3)))
ERROR: ArgumentError: matrix cannot be represented as Bidiagonal
Stacktrace:
 [1] Bidiagonal(::UpperTriangular{Float64,Array{Float64,2}}) at /home/julia/julia/usr/share/julia/stdlib/v1.1/LinearAlgebra/src/special.jl:51
 [2] top-level scope at none:0

julia> Bidiagonal(UpperTriangular(ones(3,3)), :U)
3×3 Bidiagonal{Float64,Array{Float64,1}}:
 1.0  1.0   ⋅ 
  ⋅   1.0  1.0
  ⋅    ⋅   1.0
julia> Tridiagonal(ones(3,3))
3×3 Tridiagonal{Float64,Array{Float64,1}}:
 1.0  1.0   ⋅ 
 1.0  1.0  1.0
  ⋅   1.0  1.0

julia> Tridiagonal(UpperTriangular(ones(3,3)))
ERROR: ArgumentError: matrix cannot be represented as Tridiagonal
Stacktrace:
 [1] Tridiagonal(::UpperTriangular{Float64,Array{Float64,2}}) at /home/julia/julia/usr/share/julia/stdlib/v1.1/LinearAlgebra/src/special.jl:56
 [2] top-level scope at none:0

So the constructors for *Diagonal with one argument are especially critical, when the input argument is a 'special matrix', in case they require the input data to have the expected shape in advance. But if the input is another AbstractMatrix, there is no such check, but the unexpected data are simply ignored.

For other constructors, i.e. Upper/LowerTriangular, Symmetric there is also no input check for good reasons. The same is for Bidiagonal(arg, :U).

To describe the current situation the documentation for each *Diagonal would be something like:

If the constructor has exactly one argument, which isa Diagonal, Bidiagonal, Tridiagonal,
SymTridiagonal, or AbstractTriangular it acts as a conversion, that means if the shape of the
input doesn't fit the target type, an ArgumentError is thrown.
Otherwise the constructor is considered a partial copy, that means data outside the shape of
the target type are silently ignored.

I think, that is too complex to remember. It would also require extra effort, if you wanted to ignore out-of-shape data in your use case.
@StefanKarpinski, @andreasnoack

@bsxfan
Copy link

bsxfan commented Mar 21, 2019

I would like to revive this issue. My reasons, similar to the discussion above, are given in a discourse post.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs This change adds or pertains to documentation
Projects
None yet
Development

No branches or pull requests

4 participants