@@ -1387,6 +1387,248 @@ end
13871387end
13881388
13891389include (" iterators.jl" )
1390+
1391+ # https://github.com/JuliaLang/julia/pull/43334
1392+ if VERSION < v " 1.9.0-DEV.1163"
1393+ export stack
1394+
1395+ if VERSION < v " 1.1.0-DEV.590"
1396+ _tuple_axes (x) = axes (x)
1397+ _tuple_axes (@nospecialize t:: Tuple ) = (axes (t),)
1398+ else
1399+ const _tuple_axes = axes
1400+ end
1401+
1402+ """
1403+ stack(iter; [dims])
1404+
1405+ Combine a collection of arrays (or other iterable objects) of equal size
1406+ into one larger array, by arranging them along one or more new dimensions.
1407+
1408+ By default the axes of the elements are placed first,
1409+ giving `size(result) = (size(first(iter))..., size(iter)...)`.
1410+ This has the same order of elements as [`Iterators.flatten`](@ref)`(iter)`.
1411+
1412+ With keyword `dims::Integer`, instead the `i`th element of `iter` becomes the slice
1413+ [`selectdim`](@ref)`(result, dims, i)`, so that `size(result, dims) == length(iter)`.
1414+ In this case `stack` reverses the action of [`eachslice`](@ref) with the same `dims`.
1415+
1416+ The various [`cat`](@ref) functions also combine arrays. However, these all
1417+ extend the arrays' existing (possibly trivial) dimensions, rather than placing
1418+ the arrays along new dimensions.
1419+ They also accept arrays as separate arguments, rather than a single collection.
1420+
1421+ !!! compat "Julia 1.9"
1422+ This function is available in Julia 1.9, or in Compat 4.2.
1423+
1424+ # Examples
1425+ ```jldoctest
1426+ julia> vecs = (1:2, [30, 40], Float32[500, 600]);
1427+
1428+ julia> mat = stack(vecs)
1429+ 2×3 Matrix{Float32}:
1430+ 1.0 30.0 500.0
1431+ 2.0 40.0 600.0
1432+
1433+ julia> mat == hcat(vecs...) == reduce(hcat, collect(vecs))
1434+ true
1435+
1436+ julia> vec(mat) == vcat(vecs...) == reduce(vcat, collect(vecs))
1437+ true
1438+
1439+ julia> stack(zip(1:4, 10:99)) # accepts any iterators of iterators
1440+ 2×4 Matrix{Int64}:
1441+ 1 2 3 4
1442+ 10 11 12 13
1443+
1444+ julia> vec(ans) == collect(Iterators.flatten(zip(1:4, 10:99)))
1445+ true
1446+
1447+ julia> stack(vecs; dims=1) # unlike any cat function, 1st axis of vecs[1] is 2nd axis of result
1448+ 3×2 Matrix{Float32}:
1449+ 1.0 2.0
1450+ 30.0 40.0
1451+ 500.0 600.0
1452+
1453+ julia> x = rand(3,4);
1454+
1455+ julia> x == stack(eachcol(x)) == stack(eachrow(x), dims=1) # inverse of eachslice
1456+ true
1457+ ```
1458+
1459+ Higher-dimensional examples:
1460+
1461+ ```jldoctest
1462+ julia> A = rand(5, 7, 11);
1463+
1464+ julia> E = eachslice(A, dims=2); # a vector of matrices
1465+
1466+ julia> (element = size(first(E)), container = size(E))
1467+ (element = (5, 11), container = (7,))
1468+
1469+ julia> stack(E) |> size
1470+ (5, 11, 7)
1471+
1472+ julia> stack(E) == stack(E; dims=3) == cat(E...; dims=3)
1473+ true
1474+
1475+ julia> A == stack(E; dims=2)
1476+ true
1477+
1478+ julia> M = (fill(10i+j, 2, 3) for i in 1:5, j in 1:7);
1479+
1480+ julia> (element = size(first(M)), container = size(M))
1481+ (element = (2, 3), container = (5, 7))
1482+
1483+ julia> stack(M) |> size # keeps all dimensions
1484+ (2, 3, 5, 7)
1485+
1486+ julia> stack(M; dims=1) |> size # vec(container) along dims=1
1487+ (35, 2, 3)
1488+
1489+ julia> hvcat(5, M...) |> size # hvcat puts matrices next to each other
1490+ (14, 15)
1491+ ```
1492+ """
1493+ stack (iter; dims= :) = _stack (dims, iter)
1494+
1495+ """
1496+ stack(f, args...; [dims])
1497+
1498+ Apply a function to each element of a collection, and `stack` the result.
1499+ Or to several collections, [`zip`](@ref)ped together.
1500+
1501+ The function should return arrays (or tuples, or other iterators) all of the same size.
1502+ These become slices of the result, each separated along `dims` (if given) or by default
1503+ along the last dimensions.
1504+
1505+ See also [`mapslices`](@ref), [`eachcol`](@ref).
1506+
1507+ # Examples
1508+ ```jldoctest
1509+ julia> stack(c -> (c, c-32), "julia")
1510+ 2×5 Matrix{Char}:
1511+ 'j' 'u' 'l' 'i' 'a'
1512+ 'J' 'U' 'L' 'I' 'A'
1513+
1514+ julia> stack(eachrow([1 2 3; 4 5 6]), (10, 100); dims=1) do row, n
1515+ vcat(row, row .* n, row ./ n)
1516+ end
1517+ 2×9 Matrix{Float64}:
1518+ 1.0 2.0 3.0 10.0 20.0 30.0 0.1 0.2 0.3
1519+ 4.0 5.0 6.0 400.0 500.0 600.0 0.04 0.05 0.06
1520+ ```
1521+ """
1522+ stack (f, iter; dims= :) = _stack (dims, f (x) for x in iter)
1523+ stack (f, xs, yzs... ; dims= :) = _stack (dims, f (xy... ) for xy in zip (xs, yzs... ))
1524+
1525+ _stack (dims:: Union{Integer, Colon} , iter) = _stack (dims, Base. IteratorSize (iter), iter)
1526+
1527+ _stack (dims, :: Base.IteratorSize , iter) = _stack (dims, collect (iter))
1528+
1529+ function _stack (dims, :: Union{Base.HasShape, Base.HasLength} , iter)
1530+ S = Base. @default_eltype iter
1531+ T = S != Union{} ? eltype (S) : Any # Union{} occurs for e.g. stack(1,2), postpone the error
1532+ if isconcretetype (T)
1533+ _typed_stack (dims, T, S, iter)
1534+ else # Need to look inside, but shouldn't run an expensive iterator twice:
1535+ array = iter isa Union{Tuple, AbstractArray} ? iter : collect (iter)
1536+ isempty (array) && return _empty_stack (dims, T, S, iter)
1537+ T2 = mapreduce (eltype, promote_type, array)
1538+ _typed_stack (dims, T2, eltype (array), array)
1539+ end
1540+ end
1541+
1542+ function _typed_stack (:: Colon , :: Type{T} , :: Type{S} , A, Aax= _iterator_axes (A)) where {T, S}
1543+ xit = iterate (A)
1544+ nothing === xit && return _empty_stack (:, T, S, A)
1545+ x1, _ = xit
1546+ ax1 = _iterator_axes (x1)
1547+ B = similar (_ensure_array (x1), T, ax1... , Aax... )
1548+ off = firstindex (B)
1549+ len = length (x1)
1550+ while xit != = nothing
1551+ x, state = xit
1552+ _stack_size_check (x, ax1)
1553+ copyto! (B, off, x)
1554+ off += len
1555+ xit = iterate (A, state)
1556+ end
1557+ B
1558+ end
1559+
1560+ _iterator_axes (x) = _iterator_axes (x, Base. IteratorSize (x))
1561+ _iterator_axes (x, :: Base.HasLength ) = (Base. OneTo (length (x)),)
1562+ _iterator_axes (x, :: Base.IteratorSize ) = _tuple_axes (x)
1563+
1564+ # For some dims values, stack(A; dims) == stack(vec(A)), and the : path will be faster
1565+ _typed_stack (dims:: Integer , :: Type{T} , :: Type{S} , A) where {T,S} =
1566+ _typed_stack (dims, T, S, Base. IteratorSize (S), A)
1567+ _typed_stack (dims:: Integer , :: Type{T} , :: Type{S} , :: Base.HasLength , A) where {T,S} =
1568+ _typed_stack (dims, T, S, Base. HasShape {1} (), A)
1569+ function _typed_stack (dims:: Integer , :: Type{T} , :: Type{S} , :: Base.HasShape{N} , A) where {T,S,N}
1570+ if dims == N+ 1
1571+ _typed_stack (:, T, S, A, (_vec_axis (A),))
1572+ else
1573+ _dim_stack (dims, T, S, A)
1574+ end
1575+ end
1576+ _typed_stack (dims:: Integer , :: Type{T} , :: Type{S} , :: Base.IteratorSize , A) where {T,S} =
1577+ _dim_stack (dims, T, S, A)
1578+
1579+ _vec_axis (A, ax= _iterator_axes (A)) = length (ax) == 1 ? only (ax) : Base. OneTo (mapreduce (length, * , ax; init= 1 ))
1580+
1581+ @constprop :aggressive function _dim_stack (dims:: Integer , :: Type{T} , :: Type{S} , A) where {T,S}
1582+ xit = Iterators. peel (A)
1583+ nothing === xit && return _empty_stack (dims, T, S, A)
1584+ x1, xrest = xit
1585+ ax1 = _iterator_axes (x1)
1586+ N1 = length (ax1)+ 1
1587+ dims in 1 : N1 || throw (ArgumentError (string (" cannot stack slices ndims(x) = " , N1- 1 , " along dims = " , dims)))
1588+
1589+ newaxis = _vec_axis (A)
1590+ outax = ntuple (d -> d== dims ? newaxis : ax1[d - (d> dims)], N1)
1591+ B = similar (_ensure_array (x1), T, outax... )
1592+
1593+ if dims == 1
1594+ _dim_stack! (Val (1 ), B, x1, xrest)
1595+ elseif dims == 2
1596+ _dim_stack! (Val (2 ), B, x1, xrest)
1597+ else
1598+ _dim_stack! (Val (dims), B, x1, xrest)
1599+ end
1600+ B
1601+ end
1602+
1603+ function _dim_stack! (:: Val{dims} , B:: AbstractArray , x1, xrest) where {dims}
1604+ before = ntuple (d -> Colon (), dims - 1 )
1605+ after = ntuple (d -> Colon (), ndims (B) - dims)
1606+
1607+ i = firstindex (B, dims)
1608+ copyto! (view (B, before... , i, after... ), x1)
1609+
1610+ for x in xrest
1611+ _stack_size_check (x, _iterator_axes (x1))
1612+ i += 1
1613+ @inbounds copyto! (view (B, before... , i, after... ), x)
1614+ end
1615+ end
1616+
1617+ @inline function _stack_size_check (x, ax1:: Tuple )
1618+ if _iterator_axes (x) != ax1
1619+ uax1 = map (UnitRange, ax1)
1620+ uaxN = map (UnitRange, _tuple_axes (x))
1621+ throw (DimensionMismatch (
1622+ string (" stack expects uniform slices, got axes(x) == " , uaxN, " while first had " , uax1)))
1623+ end
1624+ end
1625+
1626+ _ensure_array (x:: AbstractArray ) = x
1627+ _ensure_array (x) = 1 : 0 # passed to similar, makes stack's output an Array
1628+
1629+ _empty_stack (_... ) = throw (ArgumentError (" `stack` on an empty collection is not allowed" ))
1630+ end
1631+
13901632include (" deprecated.jl" )
13911633
13921634end # module Compat
0 commit comments