Open gdalle opened 4 months ago
Oh interesting. I'll try to take a look at it soon. Unfortunately it probably won't be this week as I'm a little swamped with work stuff. In the meantime, I think the old reduce(hcat, [x, y])
will work, it's probably just not as efficient. Thanks for finding this!
Thanks for your answer @jonniedie!
I tried to help by digging a little more, and I think the problem lies with the definition of similar
for ComponentArray
s. Here's what happens at the beginning of the problematic function _typed_stack
in the above MWE:
julia> ax1 = Base._iterator_axes(x)
(1:1:2,)
julia> Aax = Base._iterator_axes([x, y])
(Base.OneTo(2),)
julia> B = similar(x, ax1..., Aax...) # should be 2x2
2×1 ComponentMatrix{Int64} with axes Axis(a = 1:2,) × FlatAxis()
138725991131840
138725991140464
In other words, the created matrix has the wrong number of columns. Note that if instead we do
julia> B = similar(x, Aax..., Aax...)
2×2 Matrix{Int64}:
1 138723500513936
2 138723500514000
then the construction is correct. Unfortunately I am struggling to go beyond this diagnosis, but hopefully this is useful for you to narrow down the issue.
The MWE @gdalle posted here works now on this PR: https://github.com/jonniedie/ComponentArrays.jl/pull/249, but the behavior is such that it ignores the names of the components in the second array (y
in the MWE):
julia> x = ComponentVector(a=[1, 2])
ComponentVector{Int64}(a = [1, 2])
julia> y = ComponentVector(b=[3, 4])
ComponentVector{Int64}(b = [3, 4])
julia> xy = stack([x, y])
2×2 ComponentMatrix{Int64} with axes Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),) × FlatAxis()
1 3
2 4
julia> xy[:a, 1]
2-element Vector{Int64}:
1
2
julia> xy[:a, 2]
2-element Vector{Int64}:
3
4
julia>
Similar for 3 vectors, only the first argument's axis matters:
julia> z = ComponentVector(c = [5, 6])
ComponentVector{Int64}(c = [5, 6])
julia> xyz = stack([x, y, z])
2×3 ComponentMatrix{Int64} with axes Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),) × FlatAxis()
1 3 5
2 4 6
julia> xyz[:a, 1]
2-element Vector{Int64}:
1
2
julia> xyz[:a, 2]
2-element Vector{Int64}:
3
4
julia> xyz[:a, 3]
2-element Vector{Int64}:
5
6
julia>
To me this is much better than erroring, and arguably you wanna stack ComponentVectors with the same structure anyway. Thank you! Hope your PR gets merged, if there's anything I can do to help either you or @jonniedie let me know
Similar for 3 vectors, only the first argument's axis matters:
FWIW, stack
demands that axes match, for all the inner objects. This property seems somewhat surprising to me:
julia> axes(x) == axes(z)
true
but if you have reasons to have it that way, then stack((x,y,z))
should probably also work without error. Presumably any of axes(x)
or axes(z)
would be acceptable?
Alternatively you could decide that, in mismatched cases, it's better not to give an unpredictable fancy axis... follow this instead:
julia> hcat(x,x)
2×2 ComponentMatrix{Int64} with axes Axis(a = 1:2,) × FlatAxis()
1 1
2 2
julia> hcat(x,z)
2×2 Matrix{Int64}:
1 5
2 6
For testing things, note also that stack
accepts iterators of iterators, and hence these should probably all work:
stack((x,y,x); dims=1) # tuple of arrays
stack(ComponentArray(a=(1,2,3), b=(4,5,6))) # array of tuples
stack(ComponentArray(z=[x,x]) for _ in 1:4) # generator of arrays
stack(x -> ComponentArray(a=x, b=[x,x]), [5 6; 7 8]; dims=2) # map then stack
@dingraha do you think your changes are compatible with what @mcabbott mentioned?
I'm actually not sure what the result of those are supposed to be 😬. But I tried each of them. Here's what I get:
First one:
julia> x = ComponentVector(a=[1, 2])
ComponentVector{Int64}(a = [1, 2])
julia> y = ComponentVector(b=[3, 4])
ComponentVector{Int64}(b = [3, 4])
julia> x_noca = getdata(x)
2-element Vector{Int64}:
1
2
julia> y_noca = getdata(y)
2-element Vector{Int64}:
3
4
julia> stack((x, y, x); dims=1)
3×2 ComponentMatrix{Int64} with axes FlatAxis() × Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),)
1 2
3 4
1 2
julia> stack((x_noca, y_noca, x_noca); dims=1)
3×2 Matrix{Int64}:
1 2
3 4
1 2
julia>
I think that's what we'd want.
Second one:
julia> stack(ComponentArray(a=(1,2,3), b=(4,5,6)))
3×2 ComponentMatrix{Int64} with axes FlatAxis() × Axis(a = 1, b = 2)
1 4
2 5
3 6
julia>
I think that does what's intended. It's the same as stacking a tuple of two vectors:
julia> stack((x, y))
2×2 ComponentMatrix{Int64} with axes Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),) × FlatAxis()
1 3
2 4
julia>
Third one:
julia> x
ComponentVector{Int64}(a = [1, 2])
julia> stack(ComponentArray(z=[x,x]) for _ in 1:4)
4×4 ComponentMatrix{Int64} with axes Axis(z = ViewAxis(1:4, PartitionedAxis(2, Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),))),) × FlatAx
is()
1 1 1 1
2 2 2 2
1 1 1 1
2 2 2 2
julia>
Not sure about that one.
Fourth one:
julia> x
ComponentVector{Int64}(a = [1, 2])
julia> stack(x -> ComponentArray(a=x, b=[x,x]), [5 6; 7 8]; dims=2)
3×4 ComponentMatrix{Int64} with axes Axis(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,)))) × FlatAxis()
5 7 6 8
5 7 6 8
5 7 6 8
julia>
Also not sure if that's what's intended.
These look fine -- or at least they look like they contain the right Matrix, ignoring special axis types.
In the 4th, x is a local variable (sorry), hence I think it ought to match this:
julia> stack(x -> [x,x,x], [5 6; 7 8]; dims=2)
3×4 Matrix{Int64}:
5 7 6 8
5 7 6 8
5 7 6 8
Perhaps without dims
would be a better test, as no earlier case returns an array with ndims > 2:
julia> stack(x -> [x,x,x], [5 6; 7 8])
3×2×2 Array{Int64, 3}:
[:, :, 1] =
5 7
5 7
5 7
[:, :, 2] =
6 8
6 8
6 8
@mcabbott Excellent, thanks for the help. Here's what I'm getting:
julia> x2
ERROR: UndefVarError: `x2` not defined
julia> stack(x2 -> [x2,x2,x2], [5 6; 7 8]; dims=2)
3×4 Matrix{Int64}:
5 7 6 8
5 7 6 8
5 7 6 8
julia> stack(x2 -> ComponentArray(a=x2, b=[x2,x2]), [5 6; 7 8]; dims=2) # map then stack
3×4 ComponentMatrix{Int64} with axes Axis(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,)))) × FlatAxis()
5 7 6 8
5 7 6 8
5 7 6 8
julia> stack(x2 -> [x2,x2,x2], [5 6; 7 8])
3×2×2 Array{Int64, 3}:
[:, :, 1] =
5 7
5 7
5 7
[:, :, 2] =
6 8
6 8
6 8
julia> stack(x2 -> ComponentArray(a=x2, b=[x2,x2]), [5 6; 7 8])
3×2×2 ComponentArray{Int64, 3, Array{Int64, 3}, Tuple{Axis{(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,))))}, FlatAxis, FlatAxis}} with in
dices 1:1:3×1:1:2×1:1:2:
[:, :, 1] =
5 7
5 7
5 7
[:, :, 2] =
6 8
6 8
6 8
julia>
I should have actually tried to index the component arrays with components. Here are the four examples, but this time I try indexing them with symbols. Sadly the third one doesn't work. :-(
julia> Xstack1 = stack((x, y, x); dims=1)
3×2 ComponentMatrix{Int64} with axes FlatAxis() × Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),)
1 2
3 4
1 2
julia> Xstack1[:, :a]
3×2 Matrix{Int64}:
1 2
3 4
1 2
julia> Xstack2 = stack(ComponentArray(a=(1,2,3), b=(4,5,6)))
3×2 ComponentMatrix{Int64} with axes FlatAxis() × Axis(a = 1, b = 2)
1 4
2 5
3 6
julia> Xstack2[:, :a]
3-element Vector{Int64}:
1
2
3
julia> Xstack2[:, :b]
3-element Vector{Int64}:
4
5
6
julia> Xstack3 = stack(ComponentArray(z=[x,x]) for _ in 1:4)
4×4 ComponentMatrix{Int64} with axes Axis(z = ViewAxis(1:4, PartitionedAxis(2, Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),))),) × FlatAx
is()
1 1 1 1
2 2 2 2
1 1 1 1
2 2 2 2
julia> getaxes(Xstack3)
(Axis(z = ViewAxis(1:4, PartitionedAxis(2, Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),))),), FlatAxis())
julia> Xstack3[:z, :]
Error showing value of type ComponentArrays.LazyArray{Any, 1, Base.Generator{Base.Generator{StepRange{Int64, Int64}, ComponentArrays.var
"#1#2"{Matrix{Int64}, Int64}}, ComponentArrays.var"#18#19"{Tuple{Axis{(a = ViewAxis(1:2, Shaped1DAxis((2,))),)}, FlatAxis}}}}:
ERROR: type NamedTuple has no field a
Stacktrace:
[1] getproperty
@ ./Base.jl:37 [inlined]
[2] getindex(#unused#::FlatAxis, s::Symbol)
@ ComponentArrays ~/projects/componentarrays_bugs/dev/ComponentArrays/src/axis.jl:169
[3] _broadcast_getindex_evalf
@ ./broadcast.jl:683 [inlined]
[4] _broadcast_getindex
@ ./broadcast.jl:656 [inlined]
[5] (::Base.Broadcast.var"#31#32"{Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple}, Nothing, typeof(getindex), Tuple{Tuple{Axis{
(a = ViewAxis(1:2, Shaped1DAxis((2,))),)}, FlatAxis}, Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple}, Nothing, typeof(ComponentA
rrays.getval), Tuple{Tuple{DataType}}}}}})(k::Int64)
@ Base.Broadcast ./broadcast.jl:1088
[6] ntuple
@ ./ntuple.jl:49 [inlined]
[7] copy
@ ./broadcast.jl:1088 [inlined]
[8] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple}, Nothing, typeof(getindex), Tuple{Tuple{Axis{(a = ViewAxis(
1:2, Shaped1DAxis((2,))),)}, FlatAxis}, Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple}, Nothing, typeof(ComponentArrays.getval),
Tuple{Tuple{DataType}}}}})
@ Base.Broadcast ./broadcast.jl:873
[9] #s40#57
@ ~/projects/componentarrays_bugs/dev/ComponentArrays/src/array_interface.jl:120 [inlined]
[10] var"#s40#57"(::Any, index_fun::Any, x::Any, idx::Any)
@ ComponentArrays ./none:0
[11] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any})
@ Core ./boot.jl:602
[12] getindex
@ ~/projects/componentarrays_bugs/dev/ComponentArrays/src/array_interface.jl:103 [inlined]
[13] getindex
@ ~/projects/componentarrays_bugs/dev/ComponentArrays/src/array_interface.jl:102 [inlined]
[14] show(io::IOContext{IOBuffer}, x::ComponentVector{Int64, SubArray{Int64, 1, Vector{Int64}, Tuple{UnitRange{Int64}}, true}, Tuple{Ax
is{(a = ViewAxis(1:2, Shaped1DAxis((2,))),)}, FlatAxis}})
@ ComponentArrays ~/projects/componentarrays_bugs/dev/ComponentArrays/src/show.jl:52
[15] sprint(f::Function, args::ComponentVector{Int64, SubArray{Int64, 1, Vector{Int64}, Tuple{UnitRange{Int64}}, true}, Tuple{Axis{(a =
ViewAxis(1:2, Shaped1DAxis((2,))),)}, FlatAxis}}; context::IOContext{IOBuffer}, sizehint::Int64)
@ Base ./strings/io.jl:112
[16] sprint
@ ./strings/io.jl:107 [inlined]
[17] alignment_from_show
@ ./show.jl:2817 [inlined]
[18] alignment(io::IOContext{IOBuffer}, x::ComponentVector{Int64, SubArray{Int64, 1, Vector{Int64}, Tuple{UnitRange{Int64}}, true}, Tup
le{Axis{(a = ViewAxis(1:2, Shaped1DAxis((2,))),)}, FlatAxis}})
@ Base ./show.jl:2836
[19] alignment(io::IOContext{IOBuffer}, X::AbstractVecOrMat, rows::Vector{Int64}, cols::Vector{Int64}, cols_if_complete::Int64, cols_ot
herwise::Int64, sep::Int64, ncols::Int64)
@ Base ./arrayshow.jl:69
[20] _print_matrix(io::IOContext{IOBuffer}, X::AbstractVecOrMat, pre::String, sep::String, post::String, hdots::String, vdots::String,
ddots::String, hmod::Int64, vmod::Int64, rowsA::UnitRange{Int64}, colsA::UnitRange{Int64})
@ Base ./arrayshow.jl:207
[21] print_matrix(io::IOContext{IOBuffer}, X::Vector{ComponentVector{Int64, SubArray{Int64, 1, Vector{Int64}, Tuple{UnitRange{Int64}},
true}, Tuple{Axis{(a = ViewAxis(1:2, Shaped1DAxis((2,))),)}, FlatAxis}}}, pre::String, sep::String, post::String, hdots::String, vdots::
String, ddots::String, hmod::Int64, vmod::Int64)
@ Base ./arrayshow.jl:171
[22] print_matrix
@ ./arrayshow.jl:171 [inlined]
[23] print_array
@ ./arrayshow.jl:358 [inlined]
[24] show(io::IOBuffer, #unused#::MIME{Symbol("text/plain")}, X::Vector{ComponentVector{Int64, SubArray{Int64, 1, Vector{Int64}, Tuple{
UnitRange{Int64}}, true}, Tuple{Axis{(a = ViewAxis(1:2, Shaped1DAxis((2,))),)}, FlatAxis}}})
@ Base ./arrayshow.jl:399
[25] __binrepr(m::MIME{Symbol("text/plain")}, x::Vector{ComponentVector{Int64, SubArray{Int64, 1, Vector{Int64}, Tuple{UnitRange{Int64}
}, true}, Tuple{Axis{(a = ViewAxis(1:2, Shaped1DAxis((2,))),)}, FlatAxis}}}, context::Nothing)
@ Base.Multimedia ./multimedia.jl:171
[26] _binrepr
@ ./multimedia.jl:0 [inlined]
[27] #repr#1
@ ./multimedia.jl:159 [inlined]
[28] repr
@ ./multimedia.jl:159 [inlined]
[29] show(io::IOContext{Base.TTY}, mime::MIME{Symbol("text/plain")}, a::ComponentArrays.LazyArray{Any, 1, Base.Generator{Base.Generator
{StepRange{Int64, Int64}, ComponentArrays.var"#1#2"{Matrix{Int64}, Int64}}, ComponentArrays.var"#18#19"{Tuple{Axis{(a = ViewAxis(1:2, Sh
aped1DAxis((2,))),)}, FlatAxis}}}})
@ ComponentArrays ~/projects/componentarrays_bugs/dev/ComponentArrays/src/lazyarray.jl:43
[30] (::REPL.var"#55#56"{REPL.REPLDisplay{REPL.LineEditREPL}, MIME{Symbol("text/plain")}, Base.RefValue{Any}})(io::Any)
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:276
[31] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:557
[32] display(d::REPL.REPLDisplay, mime::MIME{Symbol("text/plain")}, x::Any)
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:262
[33] display
@ ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:281 [inlined]
[34] display(x::Any)
@ Base.Multimedia ./multimedia.jl:340
[35] #invokelatest#2
@ ./essentials.jl:819 [inlined]
[36] invokelatest
@ ./essentials.jl:816 [inlined]
[37] print_response(errio::IO, response::Any, show_value::Bool, have_color::Bool, specialdisplay::Union{Nothing, AbstractDisplay})
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:305
[38] (::REPL.var"#57#58"{REPL.LineEditREPL, Pair{Any, Bool}, Bool, Bool})(io::Any)
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:287
[39] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:557
[40] print_response(repl::REPL.AbstractREPL, response::Any, show_value::Bool, have_color::Bool)
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:285
[41] (::REPL.var"#do_respond#80"{Bool, Bool, REPL.var"#93#103"{REPL.LineEditREPL, REPL.REPLHistoryProvider}, REPL.LineEditREPL, REPL.Li
neEdit.Prompt})(s::REPL.LineEdit.MIState, buf::Any, ok::Bool)
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:899
[42] #invokelatest#2
@ ./essentials.jl:819 [inlined]
[43] invokelatest
@ ./essentials.jl:816 [inlined]
[44] run_interface(terminal::REPL.Terminals.TextTerminal, m::REPL.LineEdit.ModalInterface, s::REPL.LineEdit.MIState)
@ REPL.LineEdit ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/LineEdit.jl:2647
[45] run_frontend(repl::REPL.LineEditREPL, backend::REPL.REPLBackendRef)
@ REPL ~/local/julia/1.9.4/share/julia/stdlib/v1.9/REPL/src/REPL.jl:1300
[46] (::REPL.var"#62#68"{REPL.LineEditREPL, REPL.REPLBackendRef})()
@ REPL ./task.jl:514
julia> Xstack3[:, :]
4×4 ComponentMatrix{Int64} with axes Axis(z = ViewAxis(1:4, PartitionedAxis(2, Axis(a = ViewAxis(1:2, Shaped1DAxis((2,))),))),) × FlatAx
is()
1 1 1 1
2 2 2 2
1 1 1 1
2 2 2 2
julia> Xstack4 = stack(x -> ComponentArray(a=x, b=[x,x]), [5 6; 7 8])
3×2×2 ComponentArray{Int64, 3, Array{Int64, 3}, Tuple{Axis{(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,))))}, FlatAxis, FlatAxis}} with in
dices 1:1:3×1:1:2×1:1:2:
[:, :, 1] =
5 7
5 7
5 7
[:, :, 2] =
6 8
6 8
6 8
julia> Xstack4[:a, :, :]
2×2 Matrix{Int64}:
5 6
7 8
julia> Xstack4[:b, :, :]
2×2×2 Array{Int64, 3}:
[:, :, 1] =
5 7
5 7
[:, :, 2] =
6 8
6 8
julia>
Expected behavior:
Observed behavior: