-
Notifications
You must be signed in to change notification settings - Fork 151
Adds more Kronecker product specialisations #407
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
Conversation
That's a good question. Our story with |
src/linalg.jl
Outdated
if prod(outsize) > length_limit | ||
return :( SizedMatrix{$(outsize[1]),$(outsize[2])}( kron(drop_sdims(a), drop_sdims(b)) ) ) | ||
end | ||
#rows = [:(vcat($([:(a[$(LinearIndices(SA)[i])]*b) for i=1:SA[1]]...)))] |
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.
Dead code
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.
Ooops
src/linalg.jl
Outdated
end | ||
end | ||
|
||
@inline kron(a::StaticMatrix, b::RowVector{<:Number,<:StaticArray}) = _kron_mat_x_vec(_length_limit, Size(a'), Size(b'), a', b')' |
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.
Probably more direct to reshape b
into a matrix... but I don't think we have a method for that?
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.
Are you suggesting that we could reshape b
into a 1 x 3
matrix and then dispatch on the kron
that operates on two static matrices?
It seems that there is no method currently defined for that.
For example, with
s = @SVector [1, 2, 3];
reshape(s',(1,3))
1×3 Base.ReshapedArray{Int64,2,RowVector{Int64,StaticArrays.SArray{Tuple{3},Int64,1,3}},Tuple{}}:
1 2 3
I get this Base.ReshapedArray
type instead of the required StaticMatrix
.
src/linalg.jl
Outdated
if prod(outsize) > length_limit | ||
return :( SizedVector{$(outsize[1])}( kron(drop_sdims(a), drop_sdims(b)) ) ) | ||
end | ||
rows = [:(vcat($([:(a[$(LinearIndices(SA)[i])]*b) for i=1:SA[1]]...)))] |
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.
What's the purpose of the outermost array?
I'm curious about how good (performant) vcat
is with four or more arguments.
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.
Is vcat
generally slower than hcat
? I was trying to imitate what was already done for the kron(StaticMatrix, StaticMatrix)
.
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.
Now that you pointed it out I realize that the outer array serves no purpose. Its an artifact of my attempt at molding the code that was already there for the Kronecker product between two matrices. I've removed it from other parts of the code as well.
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.
Regarding vcat
, I mean we have a fast version of vcat(a, b)
but not necessarily of vcat(a, b, c, d, e, f, g)
(we should just check the generated code using @code_native
looks relatively optimal over a variety of sizes).
The pattern elsewhere is mostly to construct the final array in one go (and use similar_type
), rather than make temporary values and concatenate them together.
Codecov Report
@@ Coverage Diff @@
## master #407 +/- ##
=======================================
Coverage 92.15% 92.15%
=======================================
Files 37 37
Lines 2843 2843
=======================================
Hits 2620 2620
Misses 223 223 Continue to review full report at Codecov.
|
I've removed the dead code and the superfluous outermost array. Would you like me to try to avoid |
If were to do something about |
Hi Andy, I've been trying to figure out what you mean but to no avail. It is safe to assume I know nothing about meta programming, and am still learning aspects of Julia. The only way that I can see how one can construct the entire output array of the Kronecker product that will avoid the use @inline kron(a::StaticMatrix, b::StaticMatrix) = _kron(_length_limit, Size(a), Size(b), a, b)
@generated function _kron(::Length{length_limit}, ::Size{SA}, ::Size{SB}, a, b) where {length_limit,SA,SB}
outsize = SA .* SB
if prod(outsize) > length_limit
return :( SizedMatrix{$(outsize[1]),$(outsize[2])}( kron(drop_sdims(a), drop_sdims(b)) ) )
end
M = [ a[ia,ja] * b[ib,jb] for ia in 1:SA[1], ja in 1:SA[2], ib in 1:SB[1], jb in 1:SB[2]]
return quote
@_inline_meta
@inbounds return similar_type($(M),Size($(outsize)))($(M))
end
end However, this give the error ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type StaticArrays.SArray{Tuple{2,2},Int64,2,4}
This may have arisen from a call to the constructor StaticArrays.SArray{Tuple{2,2},Int64,2,4}(...),
since type constructors fall back to convert methods. with the following input A = @SMatrix [1 2; 3 4]
kron(A,A) On the other hand, if I test the code in my own script (outside of the generated function) it seems to work: M = [ A[ia,ja] * A[ib,jb] for ia in 1:2, ja in 1:2, ib in 1:2, jb in 1:2]
N = similar_type(M,Size((4,4)))(M)
4×4 StaticArrays.SArray{Tuple{4,4},Int64,2,16}:
1 3 2 4
3 9 6 12
2 6 4 8
4 12 8 16 I also don't understand how precisely one avoids allocations. If I use a comprehension then will it not be allocating on the heap automatically? On the other hand, the existing code also uses a comprehension of rows which seems not to allocate... I guess I don't understand the design principle behind what makes this package work. I'd appreciate if you could give me a more detailed example of how you would like me to modify the current implementation. Also, what is the reason for the error in the generated function but not in my own script? Best wishes, Zygmunt |
Apologies for brevity, but try something like |
Yes, I need to rethink the ordering and reshaping of the comprehension but unfortunately I still get an error even after I make your suggested amendment. ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type StaticArrays.SArray{Tuple{2,2},Int64,2,4}
This may have arisen from a call to the constructor StaticArrays.SArray{Tuple{2,2},Int64,2,4}(...),
since type constructors fall back to convert methods.
Stacktrace:
[1] getindex(::Type{StaticArrays.SArray{Tuple{2,2},Int64,2,4}}, ::Int64, ::Int64) at ./array.jl:208
[2] (::StaticArrays.##285#286{DataType,DataType})(::NTuple{4,Int64}) at ./<missing>:0
[3] collect(::Base.Generator{Base.Iterators.Prod{UnitRange{Int64},Base.Iterators.Prod{UnitRange{Int64},Base.Iterators.Prod2{UnitRange{Int64},UnitRange{Int64}}}},StaticArrays.##285#286{DataType,DataType}}) at ./array.jl:475
[4] _kron(...) at /home/zygmunt/.julia/v0.6/StaticArrays/src/linalg.jl:364
[5] kron(::StaticArrays.SArray{Tuple{2,2},Int64,2,4}, ::StaticArrays.SArray{Tuple{2,2},Int64,2,4}) at /home/zygmunt/.julia/v0.6/StaticArrays/src/linalg.jl:355
[6] macro expansion at /home/zygmunt/.julia/v0.6/Atom/src/repl.jl:118 [inlined]
[7] anonymous at ./<missing>:? That's with the following return return quote
@_inline_meta
@inbounds return similar_type($a, Size($(outsize)))(tuple($(M...)))
end The stack trace points to this line (with the current incorrectly ordered comprehension) M = [ a[ia,ja] * b[ib,jb] for ia in 1:SA[1], ja in 1:SA[2], ib in 1:SB[1], jb in 1:SB[2]] |
The correct order of the comprehension is as follows: M = [ A[ia,ja] * B[ib,jb] for ib in 1:SB[1],ia in 1:SA[1],jb in 1:SB[2],ja in 1:SA[2]] if one wants to use A quick test... A = @SMatrix [1 2; 3 4]
B = @SMatrix [5 6; 7 8]
P = [1 2; 3 4]
Q = [5 6; 7 8]
M = [ A[ia,ja] * B[ib,jb] for ib in 1:SB[1],ia in 1:SA[1],jb in 1:SB[2],ja in 1:SA[2]]
N = similar_type(M,Size((4,4)))(M)
@test N == kron(P,Q)
Test Passed
A = @SMatrix [5 6; 7 8]
B = @SMatrix [1 2; 3 4]
M = [ A[ia,ja] * B[ib,jb] for ib in 1:SB[1],ia in 1:SA[1],jb in 1:SB[2],ja in 1:SA[2]]
N = similar_type(M,Size((4,4)))(M)
@test N == kron(Q,P)
Test Passed |
I've posted a question on the Discourse forum to see if anyone has any suggestions for resolving the error. |
Defines `kron` between two `StaticVector` types as well as between a `StaticVector` and a `StaticMatrix` type. The definitions also include cases for transposed `StaticVector` types (i.e. a `StaticVector` wrapped by a `RowVector`type).
As per your request, for each specialization the entire output array is constructed using |
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.
Great, thanks. Love the tests, and the codegen style matches the rest of the package.
While I mentioned that in the future I'd love to consider moving some logic which chooses between SizedArray
or not to similar_type
, it's still important to gate this here because just like for matrix multiplication, the generated code size grows faster than linear in the input sizes. (In fact, taking that to the extreme, the output size also grows too fast too... at some point it's better just to drop the SizedArray
and use Array
).
Defines
kron
between twoStaticVector
types as well as between aStaticVector
and aStaticMatrix
type. The definitions also include cases for transposedStaticVector
types (i.e. aStaticVector
wrapped by aRowVector
type).There are three broken tests which are due to the fact that taking the transpose of a
SizedArray
returns aSMatrix
. I've made a comment in the relevant part of the test. Not sure of this behaviour of transpose is intentional or not?