-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Adding one
for structured matrices that preserves type
#29777
Adding one
for structured matrices that preserves type
#29777
Conversation
stdlib/LinearAlgebra/src/special.jl
Outdated
one(A::Diagonal{T}) where T = Diagonal(fill!(similar(A.diag), one(T))) | ||
one(A::Bidiagonal{T}) where T = Bidiagonal(fill!(similar(A.dv), one(T)), zero(A.ev), A.uplo) | ||
one(A::Tridiagonal{T}) where T = Tridiagonal(zero(A.du), fill!(similar(A.d), one(T)), zero(A.dl)) | ||
one(A::SymTridiagonal{T}) where T = SymTridiagonal(fill!(similar(A.dv), one(T)), zero(A.ev)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar(foo)
in all of these cases is wrong. It should be similar(foo, typeof(one(T)))
The issue is that, if T
is a dimensionful type, one
returns a dimensionless type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also use fill(one(T), size(foo))
, which might be simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zero(A.dl)
etcetera is wrong for a related reason, because zero
returns a dimensionful value. You should instead use fill(zero(one(T)), size(A.dl))
or similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also use fill(one(T), size(foo)), which might be simpler.
No, this would always return Vector
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to test the dimensionful case … see the tests with Furlongs
in the test/triangular.jl
file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fredrikekre is correct about (not) using fill(one(T), size(foo))
. This would not preserve the underlying container type (as an example: sparsevector).
As for the dimensionless type, using similar should an abstractvector with the given eltype, which then promotes the result of one
to the appropriate dimensionful type.
For example:
julia> x = Furlong(3)
Furlong{1,Int64}(3)
julia> D = Diagonal([x, x, x])
3×3 Diagonal{Furlong{1,Int64},Array{Furlong{1,Int64},1}}:
Furlong{1,Int64}(3) ⋅ ⋅
⋅ Furlong{1,Int64}(3) ⋅
⋅ ⋅ Furlong{1,Int64}(3)
julia> one(D)
3×3 Diagonal{Furlong{1,Int64},Array{Furlong{1,Int64},1}}:
Furlong{1,Int64}(1) ⋅ ⋅
⋅ Furlong{1,Int64}(1) ⋅
⋅ ⋅ Furlong{1,Int64}(1)
julia> y = Furlong{2}(3)
Furlong{2,Int64}(3)
julia> E = Diagonal([y, y, y])
3×3 Diagonal{Furlong{2,Int64},Array{Furlong{2,Int64},1}}:
Furlong{2,Int64}(3) ⋅ ⋅
⋅ Furlong{2,Int64}(3) ⋅
⋅ ⋅ Furlong{2,Int64}(3)
julia> one(E)
3×3 Diagonal{Furlong{2,Int64},Array{Furlong{2,Int64},1}}:
Furlong{2,Int64}(1) ⋅ ⋅
⋅ Furlong{2,Int64}(1) ⋅
⋅ ⋅ Furlong{2,Int64}(1)
Is this not the behavior that we want?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one
should return a multiplicative identity, so it should be unitless, e.g.
julia> x = Furlong(3)
Furlong{1,Int64}(3)
julia> one(x)
1
julia> x * one(x) == x
true
and thus,
julia> D = Diagonal([x, x, x])
3×3 Diagonal{Furlong{1,Int64},Array{Furlong{1,Int64},1}}:
Furlong{1,Int64}(3) ⋅ ⋅
⋅ Furlong{1,Int64}(3) ⋅
⋅ ⋅ Furlong{1,Int64}(3)
julia> one(D)
3×3 Diagonal{Int64,Array{Int64,1}}: <-- dimensionless
1 ⋅ ⋅
⋅ 1 ⋅
⋅ ⋅ 1
should be true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I interpreted @stevengj 's comment the opposite way. I will update this and add some tests with types that have a dimension.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would not preserve the underlying container type (as an example: sparsevector).
Who cares? Why does the underlying container type matter? An array of ones is not sparse anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way, it would be consistent with zero
. Not that I think making it a vector is a bad idea though (especially considering the behavior of zero
and one
for these matrices backed by ranges).
julia> x=sprand(Float64, 10, 10, .1)
10×10 SparseMatrixCSC{Float64,Int64} with 5 stored entries:
[6 , 1] = 0.999066
[4 , 4] = 0.45223
[3 , 5] = 0.187222
[6 , 6] = 0.87619
[8 , 9] = 0.926811
julia> zero(x)
10×10 SparseMatrixCSC{Float64,Int64} with 5 stored entries:
[6 , 1] = 0.0
[4 , 4] = 0.0
[3 , 5] = 0.0
[6 , 6] = 0.0
[8 , 9] = 0.0
julia> one(x)
10×10 SparseMatrixCSC{Float64,Int64} with 10 stored entries:
[1 , 1] = 1.0
[2 , 2] = 1.0
[3 , 3] = 1.0
[4 , 4] = 1.0
[5 , 5] = 1.0
[6 , 6] = 1.0
[7 , 7] = 1.0
[8 , 8] = 1.0
[9 , 9] = 1.0
[10, 10] = 1.0
julia> UpperTriangular(x)
10×10 UpperTriangular{Float64,SparseMatrixCSC{Float64,Int64}}:
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
⋅ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ 0.0 0.0 0.187222 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ 0.45223 0.0 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ 0.0 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ 0.87619 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0 0.926811 0.0
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0
julia> zero(UpperTriangular(x))
10×10 UpperTriangular{Float64,SparseMatrixCSC{Float64,Int64}}:
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
⋅ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ 0.0 0.0 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ 0.0 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ 0.0 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0 0.0
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0
This is great! Is it ready? |
Sorry, I've been afk working on my thesis. @stevengj approved it but I'd like to know the consensus on preserving container type in these sorts of operations. |
Seems great to me! Unlike |
Shouldn't |
No, that's been claimed over there but it's far from concluded. |
Also, maintaining the input type is what these definitions do... or were you just supporting the change? |
I have given an example in Please have a look. @StefanKarpinski |
Yeah, I saw it, it's not really definitive. |
Sorry to ping, but I believe this is ready for a final review. @stevengj @fredrikekre @mschauer |
LGTM, but are we allowed to merged such (technically) breaking changes? Will we then revert if it breaks some package? |
Yes, mark it as “minor change”. We’ll run PkgEval before releasing 1.1. |
Bump on this one. The CI failure seems erroneous. |
Yes, macOS travis failure seems to be a network glitch. (No point in restarting, since nowadays Travis macOS is failing for another reason.) |
@mcognetta: would you mind making a PR to add a NEWS item about this? |
Yes, I'll add it shortly. |
As with several other PRs,
one
falls back to generic methods for structured matrices and it does not maintain type (zero
does not have these problems).This PR adds specialized
one
methods that preserve input type for structured matrices (Diagonal
,Bidiagonal
,Tridiagonal
,SymTridiagonal
).It should be noted that when the container type is a range, it gets promoted to a
Vector
, which mimics the behavior orzero
. Otherwise, the container type is preserved.Some times:
v1.0
After PR