Line | Exclusive | Inclusive | Code |
---|---|---|---|
1 | # This file is a part of Julia. License is MIT: https://julialang.org/license | ||
2 | |||
3 | ## Basic functions ## | ||
4 | |||
5 | """ | ||
6 | AbstractArray{T,N} | ||
7 | |||
8 | Supertype for `N`-dimensional arrays (or array-like types) with elements of type `T`. | ||
9 | [`Array`](@ref) and other types are subtypes of this. See the manual section on the | ||
10 | [`AbstractArray` interface](@ref man-interface-array). | ||
11 | |||
12 | See also: [`AbstractVector`](@ref), [`AbstractMatrix`](@ref), [`eltype`](@ref), [`ndims`](@ref). | ||
13 | """ | ||
14 | AbstractArray | ||
15 | |||
16 | convert(::Type{T}, a::T) where {T<:AbstractArray} = a | ||
17 | convert(::Type{AbstractArray{T}}, a::AbstractArray) where {T} = AbstractArray{T}(a)::AbstractArray{T} | ||
18 | convert(::Type{AbstractArray{T,N}}, a::AbstractArray{<:Any,N}) where {T,N} = AbstractArray{T,N}(a)::AbstractArray{T,N} | ||
19 | |||
20 | """ | ||
21 | size(A::AbstractArray, [dim]) | ||
22 | |||
23 | Return a tuple containing the dimensions of `A`. Optionally you can specify a | ||
24 | dimension to just get the length of that dimension. | ||
25 | |||
26 | Note that `size` may not be defined for arrays with non-standard indices, in which case [`axes`](@ref) | ||
27 | may be useful. See the manual chapter on [arrays with custom indices](@ref man-custom-indices). | ||
28 | |||
29 | See also: [`length`](@ref), [`ndims`](@ref), [`eachindex`](@ref), [`sizeof`](@ref). | ||
30 | |||
31 | # Examples | ||
32 | ```jldoctest | ||
33 | julia> A = fill(1, (2,3,4)); | ||
34 | |||
35 | julia> size(A) | ||
36 | (2, 3, 4) | ||
37 | |||
38 | julia> size(A, 2) | ||
39 | 3 | ||
40 | ``` | ||
41 | """ | ||
42 | size(t::AbstractArray{T,N}, d) where {T,N} = d::Integer <= N ? size(t)[d] : 1 | ||
43 | |||
44 | """ | ||
45 | axes(A, d) | ||
46 | |||
47 | Return the valid range of indices for array `A` along dimension `d`. | ||
48 | |||
49 | See also [`size`](@ref), and the manual chapter on [arrays with custom indices](@ref man-custom-indices). | ||
50 | |||
51 | # Examples | ||
52 | |||
53 | ```jldoctest | ||
54 | julia> A = fill(1, (5,6,7)); | ||
55 | |||
56 | julia> axes(A, 2) | ||
57 | Base.OneTo(6) | ||
58 | |||
59 | julia> axes(A, 4) == 1:1 # all dimensions d > ndims(A) have size 1 | ||
60 | true | ||
61 | ``` | ||
62 | |||
63 | # Usage note | ||
64 | |||
65 | Each of the indices has to be an `AbstractUnitRange{<:Integer}`, but at the same time can be | ||
66 | a type that uses custom indices. So, for example, if you need a subset, use generalized | ||
67 | indexing constructs like `begin`/`end` or [`firstindex`](@ref)/[`lastindex`](@ref): | ||
68 | |||
69 | ```julia | ||
70 | ix = axes(v, 1) | ||
71 | ix[2:end] # will work for eg Vector, but may fail in general | ||
72 | ix[(begin+1):end] # works for generalized indexes | ||
73 | ``` | ||
74 | """ | ||
75 | function axes(A::AbstractArray{T,N}, d) where {T,N} | ||
76 | @inline | ||
77 | d::Integer <= N ? axes(A)[d] : OneTo(1) | ||
78 | end | ||
79 | |||
80 | """ | ||
81 | axes(A) | ||
82 | |||
83 | Return the tuple of valid indices for array `A`. | ||
84 | |||
85 | See also: [`size`](@ref), [`keys`](@ref), [`eachindex`](@ref). | ||
86 | |||
87 | # Examples | ||
88 | |||
89 | ```jldoctest | ||
90 | julia> A = fill(1, (5,6,7)); | ||
91 | |||
92 | julia> axes(A) | ||
93 | (Base.OneTo(5), Base.OneTo(6), Base.OneTo(7)) | ||
94 | ``` | ||
95 | """ | ||
96 | function axes(A) | ||
97 | @inline | ||
98 | map(oneto, size(A)) | ||
99 | end | ||
100 | |||
101 | """ | ||
102 | has_offset_axes(A) | ||
103 | has_offset_axes(A, B, ...) | ||
104 | |||
105 | Return `true` if the indices of `A` start with something other than 1 along any axis. | ||
106 | If multiple arguments are passed, equivalent to `has_offset_axes(A) | has_offset_axes(B) | ...`. | ||
107 | |||
108 | See also [`require_one_based_indexing`](@ref). | ||
109 | """ | ||
110 | has_offset_axes(A) = _any_tuple(x->Int(first(x))::Int != 1, false, axes(A)...) | ||
111 | has_offset_axes(A::AbstractVector) = Int(firstindex(A))::Int != 1 # improve performance of a common case (ranges) | ||
112 | # Use `_any_tuple` to avoid unneeded invoke. | ||
113 | # note: this could call `any` directly if the compiler can infer it | ||
114 | has_offset_axes(As...) = _any_tuple(has_offset_axes, false, As...) | ||
115 | has_offset_axes(::Colon) = false | ||
116 | has_offset_axes(::Array) = false | ||
117 | |||
118 | """ | ||
119 | require_one_based_indexing(A::AbstractArray) | ||
120 | require_one_based_indexing(A,B...) | ||
121 | |||
122 | Throw an `ArgumentError` if the indices of any argument start with something other than `1` along any axis. | ||
123 | See also [`has_offset_axes`](@ref). | ||
124 | |||
125 | !!! compat "Julia 1.2" | ||
126 | This function requires at least Julia 1.2. | ||
127 | """ | ||
128 | require_one_based_indexing(A...) = !has_offset_axes(A...) || throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) | ||
129 | |||
130 | # Performance optimization: get rid of a branch on `d` in `axes(A, d)` | ||
131 | # for d=1. 1d arrays are heavily used, and the first dimension comes up | ||
132 | # in other applications. | ||
133 | axes1(A::AbstractArray{<:Any,0}) = OneTo(1) | ||
134 | axes1(A::AbstractArray) = (@inline; axes(A)[1]) | ||
135 | axes1(iter) = oneto(length(iter)) | ||
136 | |||
137 | """ | ||
138 | keys(a::AbstractArray) | ||
139 | |||
140 | Return an efficient array describing all valid indices for `a` arranged in the shape of `a` itself. | ||
141 | |||
142 | The keys of 1-dimensional arrays (vectors) are integers, whereas all other N-dimensional | ||
143 | arrays use [`CartesianIndex`](@ref) to describe their locations. Often the special array | ||
144 | types [`LinearIndices`](@ref) and [`CartesianIndices`](@ref) are used to efficiently | ||
145 | represent these arrays of integers and `CartesianIndex`es, respectively. | ||
146 | |||
147 | Note that the `keys` of an array might not be the most efficient index type; for maximum | ||
148 | performance use [`eachindex`](@ref) instead. | ||
149 | |||
150 | # Examples | ||
151 | ```jldoctest | ||
152 | julia> keys([4, 5, 6]) | ||
153 | 3-element LinearIndices{1, Tuple{Base.OneTo{Int64}}}: | ||
154 | 1 | ||
155 | 2 | ||
156 | 3 | ||
157 | |||
158 | julia> keys([4 5; 6 7]) | ||
159 | CartesianIndices((2, 2)) | ||
160 | ``` | ||
161 | """ | ||
162 | keys(a::AbstractArray) = CartesianIndices(axes(a)) | ||
163 | keys(a::AbstractVector) = LinearIndices(a) | ||
164 | |||
165 | """ | ||
166 | keytype(T::Type{<:AbstractArray}) | ||
167 | keytype(A::AbstractArray) | ||
168 | |||
169 | Return the key type of an array. This is equal to the | ||
170 | [`eltype`](@ref) of the result of `keys(...)`, and is provided | ||
171 | mainly for compatibility with the dictionary interface. | ||
172 | |||
173 | # Examples | ||
174 | ```jldoctest | ||
175 | julia> keytype([1, 2, 3]) == Int | ||
176 | true | ||
177 | |||
178 | julia> keytype([1 2; 3 4]) | ||
179 | CartesianIndex{2} | ||
180 | ``` | ||
181 | |||
182 | !!! compat "Julia 1.2" | ||
183 | For arrays, this function requires at least Julia 1.2. | ||
184 | """ | ||
185 | keytype(a::AbstractArray) = keytype(typeof(a)) | ||
186 | keytype(::Type{Union{}}, slurp...) = eltype(Union{}) | ||
187 | |||
188 | keytype(A::Type{<:AbstractArray}) = CartesianIndex{ndims(A)} | ||
189 | keytype(A::Type{<:AbstractVector}) = Int | ||
190 | |||
191 | valtype(a::AbstractArray) = valtype(typeof(a)) | ||
192 | valtype(::Type{Union{}}, slurp...) = eltype(Union{}) | ||
193 | |||
194 | """ | ||
195 | valtype(T::Type{<:AbstractArray}) | ||
196 | valtype(A::AbstractArray) | ||
197 | |||
198 | Return the value type of an array. This is identical to [`eltype`](@ref) and is | ||
199 | provided mainly for compatibility with the dictionary interface. | ||
200 | |||
201 | # Examples | ||
202 | ```jldoctest | ||
203 | julia> valtype(["one", "two", "three"]) | ||
204 | String | ||
205 | ``` | ||
206 | |||
207 | !!! compat "Julia 1.2" | ||
208 | For arrays, this function requires at least Julia 1.2. | ||
209 | """ | ||
210 | valtype(A::Type{<:AbstractArray}) = eltype(A) | ||
211 | |||
212 | prevind(::AbstractArray, i::Integer) = Int(i)-1 | ||
213 | nextind(::AbstractArray, i::Integer) = Int(i)+1 | ||
214 | |||
215 | |||
216 | """ | ||
217 | eltype(type) | ||
218 | |||
219 | Determine the type of the elements generated by iterating a collection of the given `type`. | ||
220 | For dictionary types, this will be a `Pair{KeyType,ValType}`. The definition | ||
221 | `eltype(x) = eltype(typeof(x))` is provided for convenience so that instances can be passed | ||
222 | instead of types. However the form that accepts a type argument should be defined for new | ||
223 | types. | ||
224 | |||
225 | See also: [`keytype`](@ref), [`typeof`](@ref). | ||
226 | |||
227 | # Examples | ||
228 | ```jldoctest | ||
229 | julia> eltype(fill(1f0, (2,2))) | ||
230 | Float32 | ||
231 | |||
232 | julia> eltype(fill(0x1, (2,2))) | ||
233 | UInt8 | ||
234 | ``` | ||
235 | """ | ||
236 | eltype(::Type) = Any | ||
237 | eltype(::Type{Bottom}, slurp...) = throw(ArgumentError("Union{} does not have elements")) | ||
238 | eltype(x) = eltype(typeof(x)) | ||
239 | eltype(::Type{<:AbstractArray{E}}) where {E} = @isdefined(E) ? E : Any | ||
240 | |||
241 | """ | ||
242 | elsize(type) | ||
243 | |||
244 | Compute the memory stride in bytes between consecutive elements of [`eltype`](@ref) | ||
245 | stored inside the given `type`, if the array elements are stored densely with a | ||
246 | uniform linear stride. | ||
247 | |||
248 | # Examples | ||
249 | ```jldoctest | ||
250 | julia> Base.elsize(rand(Float32, 10)) | ||
251 | 4 | ||
252 | ``` | ||
253 | """ | ||
254 | elsize(A::AbstractArray) = elsize(typeof(A)) | ||
255 | |||
256 | """ | ||
257 | ndims(A::AbstractArray) -> Integer | ||
258 | |||
259 | Return the number of dimensions of `A`. | ||
260 | |||
261 | See also: [`size`](@ref), [`axes`](@ref). | ||
262 | |||
263 | # Examples | ||
264 | ```jldoctest | ||
265 | julia> A = fill(1, (3,4,5)); | ||
266 | |||
267 | julia> ndims(A) | ||
268 | 3 | ||
269 | ``` | ||
270 | """ | ||
271 | ndims(::AbstractArray{T,N}) where {T,N} = N | ||
272 | ndims(::Type{<:AbstractArray{<:Any,N}}) where {N} = N | ||
273 | ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) | ||
274 | |||
275 | """ | ||
276 | length(collection) -> Integer | ||
277 | |||
278 | Return the number of elements in the collection. | ||
279 | |||
280 | Use [`lastindex`](@ref) to get the last valid index of an indexable collection. | ||
281 | |||
282 | See also: [`size`](@ref), [`ndims`](@ref), [`eachindex`](@ref). | ||
283 | |||
284 | # Examples | ||
285 | ```jldoctest | ||
286 | julia> length(1:5) | ||
287 | 5 | ||
288 | |||
289 | julia> length([1, 2, 3, 4]) | ||
290 | 4 | ||
291 | |||
292 | julia> length([1 2; 3 4]) | ||
293 | 4 | ||
294 | ``` | ||
295 | """ | ||
296 | length | ||
297 | |||
298 | """ | ||
299 | length(A::AbstractArray) | ||
300 | |||
301 | Return the number of elements in the array, defaults to `prod(size(A))`. | ||
302 | |||
303 | # Examples | ||
304 | ```jldoctest | ||
305 | julia> length([1, 2, 3, 4]) | ||
306 | 4 | ||
307 | |||
308 | julia> length([1 2; 3 4]) | ||
309 | 4 | ||
310 | ``` | ||
311 | """ | ||
312 | length(t::AbstractArray) = (@inline; prod(size(t))) | ||
313 | |||
314 | # `eachindex` is mostly an optimization of `keys` | ||
315 | eachindex(itrs...) = keys(itrs...) | ||
316 | |||
317 | # eachindex iterates over all indices. IndexCartesian definitions are later. | ||
318 | eachindex(A::AbstractVector) = (@inline(); axes1(A)) | ||
319 | |||
320 | |||
321 | @noinline function throw_eachindex_mismatch_indices(::IndexLinear, inds...) | ||
322 | throw(DimensionMismatch("all inputs to eachindex must have the same indices, got $(join(inds, ", ", " and "))")) | ||
323 | end | ||
324 | @noinline function throw_eachindex_mismatch_indices(::IndexCartesian, inds...) | ||
325 | throw(DimensionMismatch("all inputs to eachindex must have the same axes, got $(join(inds, ", ", " and "))")) | ||
326 | end | ||
327 | |||
328 | """ | ||
329 | eachindex(A...) | ||
330 | eachindex(::IndexStyle, A::AbstractArray...) | ||
331 | |||
332 | Create an iterable object for visiting each index of an `AbstractArray` `A` in an efficient | ||
333 | manner. For array types that have opted into fast linear indexing (like `Array`), this is | ||
334 | simply the range `1:length(A)` if they use 1-based indexing. | ||
335 | For array types that have not opted into fast linear indexing, a specialized Cartesian | ||
336 | range is typically returned to efficiently index into the array with indices specified | ||
337 | for every dimension. | ||
338 | |||
339 | In general `eachindex` accepts arbitrary iterables, including strings and dictionaries, and returns | ||
340 | an iterator object supporting arbitrary index types (e.g. unevenly spaced or non-integer indices). | ||
341 | |||
342 | If `A` is `AbstractArray` it is possible to explicitly specify the style of the indices that | ||
343 | should be returned by `eachindex` by passing a value having `IndexStyle` type as its first argument | ||
344 | (typically `IndexLinear()` if linear indices are required or `IndexCartesian()` if Cartesian | ||
345 | range is wanted). | ||
346 | |||
347 | If you supply more than one `AbstractArray` argument, `eachindex` will create an | ||
348 | iterable object that is fast for all arguments (typically a [`UnitRange`](@ref) | ||
349 | if all inputs have fast linear indexing, a [`CartesianIndices`](@ref) otherwise). | ||
350 | If the arrays have different sizes and/or dimensionalities, a `DimensionMismatch` exception | ||
351 | will be thrown. | ||
352 | |||
353 | See also [`pairs`](@ref)`(A)` to iterate over indices and values together, | ||
354 | and [`axes`](@ref)`(A, 2)` for valid indices along one dimension. | ||
355 | |||
356 | # Examples | ||
357 | ```jldoctest | ||
358 | julia> A = [10 20; 30 40]; | ||
359 | |||
360 | julia> for i in eachindex(A) # linear indexing | ||
361 | println("A[", i, "] == ", A[i]) | ||
362 | end | ||
363 | A[1] == 10 | ||
364 | A[2] == 30 | ||
365 | A[3] == 20 | ||
366 | A[4] == 40 | ||
367 | |||
368 | julia> for i in eachindex(view(A, 1:2, 1:1)) # Cartesian indexing | ||
369 | println(i) | ||
370 | end | ||
371 | CartesianIndex(1, 1) | ||
372 | CartesianIndex(2, 1) | ||
373 | ``` | ||
374 | """ | ||
375 | eachindex(A::AbstractArray) = (@inline(); eachindex(IndexStyle(A), A)) | ||
376 | |||
377 | function eachindex(A::AbstractArray, B::AbstractArray) | ||
378 | @inline | ||
379 | eachindex(IndexStyle(A,B), A, B) | ||
380 | end | ||
381 | function eachindex(A::AbstractArray, B::AbstractArray...) | ||
382 | @inline | ||
383 | eachindex(IndexStyle(A,B...), A, B...) | ||
384 | end | ||
385 | eachindex(::IndexLinear, A::AbstractArray) = (@inline; oneto(length(A))) | ||
386 | eachindex(::IndexLinear, A::AbstractVector) = (@inline; axes1(A)) | ||
387 | function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...) | ||
388 | @inline | ||
389 | indsA = eachindex(IndexLinear(), A) | ||
390 | _all_match_first(X->eachindex(IndexLinear(), X), indsA, B...) || | ||
391 | throw_eachindex_mismatch_indices(IndexLinear(), eachindex(A), eachindex.(B)...) | ||
392 | indsA | ||
393 | end | ||
394 | function _all_match_first(f::F, inds, A, B...) where F<:Function | ||
395 | @inline | ||
396 | (inds == f(A)) & _all_match_first(f, inds, B...) | ||
397 | end | ||
398 | _all_match_first(f::F, inds) where F<:Function = true | ||
399 | |||
400 | # keys with an IndexStyle | ||
401 | keys(s::IndexStyle, A::AbstractArray, B::AbstractArray...) = eachindex(s, A, B...) | ||
402 | |||
403 | """ | ||
404 | lastindex(collection) -> Integer | ||
405 | lastindex(collection, d) -> Integer | ||
406 | |||
407 | Return the last index of `collection`. If `d` is given, return the last index of `collection` along dimension `d`. | ||
408 | |||
409 | The syntaxes `A[end]` and `A[end, end]` lower to `A[lastindex(A)]` and | ||
410 | `A[lastindex(A, 1), lastindex(A, 2)]`, respectively. | ||
411 | |||
412 | See also: [`axes`](@ref), [`firstindex`](@ref), [`eachindex`](@ref), [`prevind`](@ref). | ||
413 | |||
414 | # Examples | ||
415 | ```jldoctest | ||
416 | julia> lastindex([1,2,4]) | ||
417 | 3 | ||
418 | |||
419 | julia> lastindex(rand(3,4,5), 2) | ||
420 | 4 | ||
421 | ``` | ||
422 | """ | ||
423 | lastindex(a::AbstractArray) = (@inline; last(eachindex(IndexLinear(), a))) | ||
424 | lastindex(a, d) = (@inline; last(axes(a, d))) | ||
425 | |||
426 | """ | ||
427 | firstindex(collection) -> Integer | ||
428 | firstindex(collection, d) -> Integer | ||
429 | |||
430 | Return the first index of `collection`. If `d` is given, return the first index of `collection` along dimension `d`. | ||
431 | |||
432 | The syntaxes `A[begin]` and `A[1, begin]` lower to `A[firstindex(A)]` and | ||
433 | `A[1, firstindex(A, 2)]`, respectively. | ||
434 | |||
435 | See also: [`first`](@ref), [`axes`](@ref), [`lastindex`](@ref), [`nextind`](@ref). | ||
436 | |||
437 | # Examples | ||
438 | ```jldoctest | ||
439 | julia> firstindex([1,2,4]) | ||
440 | 1 | ||
441 | |||
442 | julia> firstindex(rand(3,4,5), 2) | ||
443 | 1 | ||
444 | ``` | ||
445 | """ | ||
446 | firstindex(a::AbstractArray) = (@inline; first(eachindex(IndexLinear(), a))) | ||
447 | firstindex(a, d) = (@inline; first(axes(a, d))) | ||
448 | |||
449 | first(a::AbstractArray) = a[first(eachindex(a))] | ||
450 | |||
451 | """ | ||
452 | first(coll) | ||
453 | |||
454 | Get the first element of an iterable collection. Return the start point of an | ||
455 | [`AbstractRange`](@ref) even if it is empty. | ||
456 | |||
457 | See also: [`only`](@ref), [`firstindex`](@ref), [`last`](@ref). | ||
458 | |||
459 | # Examples | ||
460 | ```jldoctest | ||
461 | julia> first(2:2:10) | ||
462 | 2 | ||
463 | |||
464 | julia> first([1; 2; 3; 4]) | ||
465 | 1 | ||
466 | ``` | ||
467 | """ | ||
468 | function first(itr) | ||
469 | x = iterate(itr) | ||
470 | x === nothing && throw(ArgumentError("collection must be non-empty")) | ||
471 | x[1] | ||
472 | end | ||
473 | |||
474 | """ | ||
475 | first(itr, n::Integer) | ||
476 | |||
477 | Get the first `n` elements of the iterable collection `itr`, or fewer elements if `itr` is not | ||
478 | long enough. | ||
479 | |||
480 | See also: [`startswith`](@ref), [`Iterators.take`](@ref). | ||
481 | |||
482 | !!! compat "Julia 1.6" | ||
483 | This method requires at least Julia 1.6. | ||
484 | |||
485 | # Examples | ||
486 | ```jldoctest | ||
487 | julia> first(["foo", "bar", "qux"], 2) | ||
488 | 2-element Vector{String}: | ||
489 | "foo" | ||
490 | "bar" | ||
491 | |||
492 | julia> first(1:6, 10) | ||
493 | 1:6 | ||
494 | |||
495 | julia> first(Bool[], 1) | ||
496 | Bool[] | ||
497 | ``` | ||
498 | """ | ||
499 | first(itr, n::Integer) = collect(Iterators.take(itr, n)) | ||
500 | # Faster method for vectors | ||
501 | function first(v::AbstractVector, n::Integer) | ||
502 | n < 0 && throw(ArgumentError("Number of elements must be nonnegative")) | ||
503 | v[range(begin, length=min(n, checked_length(v)))] | ||
504 | end | ||
505 | |||
506 | """ | ||
507 | last(coll) | ||
508 | |||
509 | Get the last element of an ordered collection, if it can be computed in O(1) time. This is | ||
510 | accomplished by calling [`lastindex`](@ref) to get the last index. Return the end | ||
511 | point of an [`AbstractRange`](@ref) even if it is empty. | ||
512 | |||
513 | See also [`first`](@ref), [`endswith`](@ref). | ||
514 | |||
515 | # Examples | ||
516 | ```jldoctest | ||
517 | julia> last(1:2:10) | ||
518 | 9 | ||
519 | |||
520 | julia> last([1; 2; 3; 4]) | ||
521 | 4 | ||
522 | ``` | ||
523 | """ | ||
524 | last(a) = a[end] | ||
525 | |||
526 | """ | ||
527 | last(itr, n::Integer) | ||
528 | |||
529 | Get the last `n` elements of the iterable collection `itr`, or fewer elements if `itr` is not | ||
530 | long enough. | ||
531 | |||
532 | !!! compat "Julia 1.6" | ||
533 | This method requires at least Julia 1.6. | ||
534 | |||
535 | # Examples | ||
536 | ```jldoctest | ||
537 | julia> last(["foo", "bar", "qux"], 2) | ||
538 | 2-element Vector{String}: | ||
539 | "bar" | ||
540 | "qux" | ||
541 | |||
542 | julia> last(1:6, 10) | ||
543 | 1:6 | ||
544 | |||
545 | julia> last(Float64[], 1) | ||
546 | Float64[] | ||
547 | ``` | ||
548 | """ | ||
549 | last(itr, n::Integer) = reverse!(collect(Iterators.take(Iterators.reverse(itr), n))) | ||
550 | # Faster method for arrays | ||
551 | function last(v::AbstractVector, n::Integer) | ||
552 | n < 0 && throw(ArgumentError("Number of elements must be nonnegative")) | ||
553 | v[range(stop=lastindex(v), length=min(n, checked_length(v)))] | ||
554 | end | ||
555 | |||
556 | """ | ||
557 | strides(A) | ||
558 | |||
559 | Return a tuple of the memory strides in each dimension. | ||
560 | |||
561 | See also: [`stride`](@ref). | ||
562 | |||
563 | # Examples | ||
564 | ```jldoctest | ||
565 | julia> A = fill(1, (3,4,5)); | ||
566 | |||
567 | julia> strides(A) | ||
568 | (1, 3, 12) | ||
569 | ``` | ||
570 | """ | ||
571 | function strides end | ||
572 | |||
573 | """ | ||
574 | stride(A, k::Integer) | ||
575 | |||
576 | Return the distance in memory (in number of elements) between adjacent elements in dimension `k`. | ||
577 | |||
578 | See also: [`strides`](@ref). | ||
579 | |||
580 | # Examples | ||
581 | ```jldoctest | ||
582 | julia> A = fill(1, (3,4,5)); | ||
583 | |||
584 | julia> stride(A,2) | ||
585 | 3 | ||
586 | |||
587 | julia> stride(A,3) | ||
588 | 12 | ||
589 | ``` | ||
590 | """ | ||
591 | function stride(A::AbstractArray, k::Integer) | ||
592 | st = strides(A) | ||
593 | k ≤ ndims(A) && return st[k] | ||
594 | ndims(A) == 0 && return 1 | ||
595 | sz = size(A) | ||
596 | s = st[1] * sz[1] | ||
597 | for i in 2:ndims(A) | ||
598 | s += st[i] * sz[i] | ||
599 | end | ||
600 | return s | ||
601 | end | ||
602 | |||
603 | @inline size_to_strides(s, d, sz...) = (s, size_to_strides(s * d, sz...)...) | ||
604 | size_to_strides(s, d) = (s,) | ||
605 | size_to_strides(s) = () | ||
606 | |||
607 | function isstored(A::AbstractArray{<:Any,N}, I::Vararg{Integer,N}) where {N} | ||
608 | @boundscheck checkbounds(A, I...) | ||
609 | return true | ||
610 | end | ||
611 | |||
612 | # used to compute "end" for last index | ||
613 | function trailingsize(A, n) | ||
614 | s = 1 | ||
615 | for i=n:ndims(A) | ||
616 | s *= size(A,i) | ||
617 | end | ||
618 | return s | ||
619 | end | ||
620 | function trailingsize(inds::Indices, n) | ||
621 | s = 1 | ||
622 | for i=n:length(inds) | ||
623 | s *= length(inds[i]) | ||
624 | end | ||
625 | return s | ||
626 | end | ||
627 | # This version is type-stable even if inds is heterogeneous | ||
628 | function trailingsize(inds::Indices) | ||
629 | @inline | ||
630 | prod(map(length, inds)) | ||
631 | end | ||
632 | |||
633 | ## Bounds checking ## | ||
634 | |||
635 | # The overall hierarchy is | ||
636 | # `checkbounds(A, I...)` -> | ||
637 | # `checkbounds(Bool, A, I...)` -> | ||
638 | # `checkbounds_indices(Bool, IA, I)`, which recursively calls | ||
639 | # `checkindex` for each dimension | ||
640 | # | ||
641 | # See the "boundscheck" devdocs for more information. | ||
642 | # | ||
643 | # Note this hierarchy has been designed to reduce the likelihood of | ||
644 | # method ambiguities. We try to make `checkbounds` the place to | ||
645 | # specialize on array type, and try to avoid specializations on index | ||
646 | # types; conversely, `checkindex` is intended to be specialized only | ||
647 | # on index type (especially, its last argument). | ||
648 | |||
649 | """ | ||
650 | checkbounds(Bool, A, I...) | ||
651 | |||
652 | Return `true` if the specified indices `I` are in bounds for the given | ||
653 | array `A`. Subtypes of `AbstractArray` should specialize this method | ||
654 | if they need to provide custom bounds checking behaviors; however, in | ||
655 | many cases one can rely on `A`'s indices and [`checkindex`](@ref). | ||
656 | |||
657 | See also [`checkindex`](@ref). | ||
658 | |||
659 | # Examples | ||
660 | ```jldoctest | ||
661 | julia> A = rand(3, 3); | ||
662 | |||
663 | julia> checkbounds(Bool, A, 2) | ||
664 | true | ||
665 | |||
666 | julia> checkbounds(Bool, A, 3, 4) | ||
667 | false | ||
668 | |||
669 | julia> checkbounds(Bool, A, 1:3) | ||
670 | true | ||
671 | |||
672 | julia> checkbounds(Bool, A, 1:3, 2:4) | ||
673 | false | ||
674 | ``` | ||
675 | """ | ||
676 | function checkbounds(::Type{Bool}, A::AbstractArray, I...) | ||
677 | @inline | ||
678 | checkbounds_indices(Bool, axes(A), I) | ||
679 | end | ||
680 | |||
681 | # Linear indexing is explicitly allowed when there is only one (non-cartesian) index | ||
682 | function checkbounds(::Type{Bool}, A::AbstractArray, i) | ||
683 | @inline | ||
684 | checkindex(Bool, eachindex(IndexLinear(), A), i) | ||
685 | end | ||
686 | # As a special extension, allow using logical arrays that match the source array exactly | ||
687 | function checkbounds(::Type{Bool}, A::AbstractArray{<:Any,N}, I::AbstractArray{Bool,N}) where N | ||
688 | @inline | ||
689 | axes(A) == axes(I) | ||
690 | end | ||
691 | |||
692 | """ | ||
693 | checkbounds(A, I...) | ||
694 | |||
695 | Throw an error if the specified indices `I` are not in bounds for the given array `A`. | ||
696 | """ | ||
697 | function checkbounds(A::AbstractArray, I...) | ||
698 | @inline | ||
699 | checkbounds(Bool, A, I...) || throw_boundserror(A, I) | ||
700 | nothing | ||
701 | end | ||
702 | |||
703 | """ | ||
704 | checkbounds_indices(Bool, IA, I) | ||
705 | |||
706 | Return `true` if the "requested" indices in the tuple `I` fall within | ||
707 | the bounds of the "permitted" indices specified by the tuple | ||
708 | `IA`. This function recursively consumes elements of these tuples, | ||
709 | usually in a 1-for-1 fashion, | ||
710 | |||
711 | checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & | ||
712 | checkbounds_indices(Bool, IA, I) | ||
713 | |||
714 | Note that [`checkindex`](@ref) is being used to perform the actual | ||
715 | bounds-check for a single dimension of the array. | ||
716 | |||
717 | There are two important exceptions to the 1-1 rule: linear indexing and | ||
718 | CartesianIndex{N}, both of which may "consume" more than one element | ||
719 | of `IA`. | ||
720 | |||
721 | See also [`checkbounds`](@ref). | ||
722 | """ | ||
723 | function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple) | ||
724 | @inline | ||
725 | checkindex(Bool, IA[1], I[1])::Bool & checkbounds_indices(Bool, tail(IA), tail(I)) | ||
726 | end | ||
727 | function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple) | ||
728 | @inline | ||
729 | checkindex(Bool, OneTo(1), I[1])::Bool & checkbounds_indices(Bool, (), tail(I)) | ||
730 | end | ||
731 | checkbounds_indices(::Type{Bool}, IA::Tuple, ::Tuple{}) = (@inline; all(x->length(x)==1, IA)) | ||
732 | checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true | ||
733 | |||
734 | throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) | ||
735 | |||
736 | # check along a single dimension | ||
737 | """ | ||
738 | checkindex(Bool, inds::AbstractUnitRange, index) | ||
739 | |||
740 | Return `true` if the given `index` is within the bounds of | ||
741 | `inds`. Custom types that would like to behave as indices for all | ||
742 | arrays can extend this method in order to provide a specialized bounds | ||
743 | checking implementation. | ||
744 | |||
745 | See also [`checkbounds`](@ref). | ||
746 | |||
747 | # Examples | ||
748 | ```jldoctest | ||
749 | julia> checkindex(Bool, 1:20, 8) | ||
750 | true | ||
751 | |||
752 | julia> checkindex(Bool, 1:20, 21) | ||
753 | false | ||
754 | ``` | ||
755 | """ | ||
756 | checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = | ||
757 | throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) | ||
758 | checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) | ||
759 | checkindex(::Type{Bool}, inds::IdentityUnitRange, i::Real) = checkindex(Bool, inds.indices, i) | ||
760 | checkindex(::Type{Bool}, inds::OneTo{T}, i::T) where {T<:BitInteger} = unsigned(i - one(i)) < unsigned(last(inds)) | ||
761 | checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true | ||
762 | checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true | ||
763 | function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::AbstractRange) | ||
764 | @_propagate_inbounds_meta | ||
765 | isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r))) | ||
766 | end | ||
767 | checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractVector{Bool}) = indx == axes1(I) | ||
768 | checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractArray{Bool}) = false | ||
769 | function checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractArray) | ||
770 | @inline | ||
771 | b = true | ||
772 | for i in I | ||
773 | b &= checkindex(Bool, inds, i) | ||
774 | end | ||
775 | b | ||
776 | end | ||
777 | |||
778 | # See also specializations in multidimensional | ||
779 | |||
780 | ## Constructors ## | ||
781 | |||
782 | # default arguments to similar() | ||
783 | """ | ||
784 | similar(array, [element_type=eltype(array)], [dims=size(array)]) | ||
785 | |||
786 | Create an uninitialized mutable array with the given element type and size, based upon the | ||
787 | given source array. The second and third arguments are both optional, defaulting to the | ||
788 | given array's `eltype` and `size`. The dimensions may be specified either as a single tuple | ||
789 | argument or as a series of integer arguments. | ||
790 | |||
791 | Custom AbstractArray subtypes may choose which specific array type is best-suited to return | ||
792 | for the given element type and dimensionality. If they do not specialize this method, the | ||
793 | default is an `Array{element_type}(undef, dims...)`. | ||
794 | |||
795 | For example, `similar(1:10, 1, 4)` returns an uninitialized `Array{Int,2}` since ranges are | ||
796 | neither mutable nor support 2 dimensions: | ||
797 | |||
798 | ```julia-repl | ||
799 | julia> similar(1:10, 1, 4) | ||
800 | 1×4 Matrix{Int64}: | ||
801 | 4419743872 4374413872 4419743888 0 | ||
802 | ``` | ||
803 | |||
804 | Conversely, `similar(trues(10,10), 2)` returns an uninitialized `BitVector` with two | ||
805 | elements since `BitArray`s are both mutable and can support 1-dimensional arrays: | ||
806 | |||
807 | ```julia-repl | ||
808 | julia> similar(trues(10,10), 2) | ||
809 | 2-element BitVector: | ||
810 | 0 | ||
811 | 0 | ||
812 | ``` | ||
813 | |||
814 | Since `BitArray`s can only store elements of type [`Bool`](@ref), however, if you request a | ||
815 | different element type it will create a regular `Array` instead: | ||
816 | |||
817 | ```julia-repl | ||
818 | julia> similar(falses(10), Float64, 2, 4) | ||
819 | 2×4 Matrix{Float64}: | ||
820 | 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 | ||
821 | 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 | ||
822 | ``` | ||
823 | |||
824 | See also: [`undef`](@ref), [`isassigned`](@ref). | ||
825 | """ | ||
826 | similar(a::AbstractArray{T}) where {T} = similar(a, T) | ||
827 | similar(a::AbstractArray, ::Type{T}) where {T} = similar(a, T, to_shape(axes(a))) | ||
828 | similar(a::AbstractArray{T}, dims::Tuple) where {T} = similar(a, T, to_shape(dims)) | ||
829 | similar(a::AbstractArray{T}, dims::DimOrInd...) where {T} = similar(a, T, to_shape(dims)) | ||
830 | similar(a::AbstractArray, ::Type{T}, dims::DimOrInd...) where {T} = similar(a, T, to_shape(dims)) | ||
831 | # Similar supports specifying dims as either Integers or AbstractUnitRanges or any mixed combination | ||
832 | # thereof. Ideally, we'd just convert Integers to OneTos and then call a canonical method with the axes, | ||
833 | # but we don't want to require all AbstractArray subtypes to dispatch on Base.OneTo. So instead we | ||
834 | # define this method to convert supported axes to Ints, with the expectation that an offset array | ||
835 | # package will define a method with dims::Tuple{Union{Integer, UnitRange}, Vararg{Union{Integer, UnitRange}}} | ||
836 | similar(a::AbstractArray, ::Type{T}, dims::Tuple{Union{Integer, OneTo}, Vararg{Union{Integer, OneTo}}}) where {T} = similar(a, T, to_shape(dims)) | ||
837 | similar(a::AbstractArray, ::Type{T}, dims::Tuple{Integer, Vararg{Integer}}) where {T} = similar(a, T, to_shape(dims)) | ||
838 | # similar creates an Array by default | ||
839 | similar(a::AbstractArray, ::Type{T}, dims::Dims{N}) where {T,N} = Array{T,N}(undef, dims) | ||
840 | |||
841 | to_shape(::Tuple{}) = () | ||
842 | to_shape(dims::Dims) = dims | ||
843 | to_shape(dims::DimsOrInds) = map(to_shape, dims)::DimsOrInds | ||
844 | # each dimension | ||
845 | to_shape(i::Int) = i | ||
846 | to_shape(i::Integer) = Int(i) | ||
847 | to_shape(r::OneTo) = Int(last(r)) | ||
848 | to_shape(r::AbstractUnitRange) = r | ||
849 | |||
850 | """ | ||
851 | similar(storagetype, axes) | ||
852 | |||
853 | Create an uninitialized mutable array analogous to that specified by | ||
854 | `storagetype`, but with `axes` specified by the last | ||
855 | argument. | ||
856 | |||
857 | **Examples**: | ||
858 | |||
859 | similar(Array{Int}, axes(A)) | ||
860 | |||
861 | creates an array that "acts like" an `Array{Int}` (and might indeed be | ||
862 | backed by one), but which is indexed identically to `A`. If `A` has | ||
863 | conventional indexing, this will be identical to | ||
864 | `Array{Int}(undef, size(A))`, but if `A` has unconventional indexing then the | ||
865 | indices of the result will match `A`. | ||
866 | |||
867 | similar(BitArray, (axes(A, 2),)) | ||
868 | |||
869 | would create a 1-dimensional logical array whose indices match those | ||
870 | of the columns of `A`. | ||
871 | """ | ||
872 | similar(::Type{T}, dims::DimOrInd...) where {T<:AbstractArray} = similar(T, dims) | ||
873 | similar(::Type{T}, shape::Tuple{Union{Integer, OneTo}, Vararg{Union{Integer, OneTo}}}) where {T<:AbstractArray} = similar(T, to_shape(shape)) | ||
874 | similar(::Type{T}, dims::Dims) where {T<:AbstractArray} = T(undef, dims) | ||
875 | |||
876 | """ | ||
877 | empty(v::AbstractVector, [eltype]) | ||
878 | |||
879 | Create an empty vector similar to `v`, optionally changing the `eltype`. | ||
880 | |||
881 | See also: [`empty!`](@ref), [`isempty`](@ref), [`isassigned`](@ref). | ||
882 | |||
883 | # Examples | ||
884 | |||
885 | ```jldoctest | ||
886 | julia> empty([1.0, 2.0, 3.0]) | ||
887 | Float64[] | ||
888 | |||
889 | julia> empty([1.0, 2.0, 3.0], String) | ||
890 | String[] | ||
891 | ``` | ||
892 | """ | ||
893 | empty(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}() | ||
894 | |||
895 | # like empty, but should return a mutable collection, a Vector by default | ||
896 | emptymutable(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}() | ||
897 | emptymutable(itr, ::Type{U}) where {U} = Vector{U}() | ||
898 | |||
899 | """ | ||
900 | copy!(dst, src) -> dst | ||
901 | |||
902 | In-place [`copy`](@ref) of `src` into `dst`, discarding any pre-existing | ||
903 | elements in `dst`. | ||
904 | If `dst` and `src` are of the same type, `dst == src` should hold after | ||
905 | the call. If `dst` and `src` are multidimensional arrays, they must have | ||
906 | equal [`axes`](@ref). | ||
907 | |||
908 | $(_DOCS_ALIASING_WARNING) | ||
909 | |||
910 | See also [`copyto!`](@ref). | ||
911 | |||
912 | !!! compat "Julia 1.1" | ||
913 | This method requires at least Julia 1.1. In Julia 1.0 this method | ||
914 | is available from the `Future` standard library as `Future.copy!`. | ||
915 | """ | ||
916 | function copy!(dst::AbstractVector, src::AbstractVector) | ||
917 | firstindex(dst) == firstindex(src) || throw(ArgumentError( | ||
918 | "vectors must have the same offset for copy! (consider using `copyto!`)")) | ||
919 | if length(dst) != length(src) | ||
920 | resize!(dst, length(src)) | ||
921 | end | ||
922 | copyto!(dst, src) | ||
923 | end | ||
924 | |||
925 | function copy!(dst::AbstractArray, src::AbstractArray) | ||
926 | axes(dst) == axes(src) || throw(ArgumentError( | ||
927 | "arrays must have the same axes for copy! (consider using `copyto!`)")) | ||
928 | copyto!(dst, src) | ||
929 | end | ||
930 | |||
931 | ## from general iterable to any array | ||
932 | |||
933 | # This is `Experimental.@max_methods 1 function copyto! end`, which is not | ||
934 | # defined at this point in bootstrap. | ||
935 | typeof(function copyto! end).name.max_methods = UInt8(1) | ||
936 | |||
937 | function copyto!(dest::AbstractArray, src) | ||
938 | destiter = eachindex(dest) | ||
939 | y = iterate(destiter) | ||
940 | for x in src | ||
941 | y === nothing && | ||
942 | throw(ArgumentError("destination has fewer elements than required")) | ||
943 | dest[y[1]] = x | ||
944 | y = iterate(destiter, y[2]) | ||
945 | end | ||
946 | return dest | ||
947 | end | ||
948 | |||
949 | function copyto!(dest::AbstractArray, dstart::Integer, src) | ||
950 | i = Int(dstart) | ||
951 | if haslength(src) && length(dest) > 0 | ||
952 | @boundscheck checkbounds(dest, i:(i + length(src) - 1)) | ||
953 | for x in src | ||
954 | @inbounds dest[i] = x | ||
955 | i += 1 | ||
956 | end | ||
957 | else | ||
958 | for x in src | ||
959 | dest[i] = x | ||
960 | i += 1 | ||
961 | end | ||
962 | end | ||
963 | return dest | ||
964 | end | ||
965 | |||
966 | # copy from an some iterable object into an AbstractArray | ||
967 | function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer) | ||
968 | if (sstart < 1) | ||
969 | throw(ArgumentError(LazyString("source start offset (",sstart,") is < 1"))) | ||
970 | end | ||
971 | y = iterate(src) | ||
972 | for j = 1:(sstart-1) | ||
973 | if y === nothing | ||
974 | throw(ArgumentError(LazyString( | ||
975 | "source has fewer elements than required, ", | ||
976 | "expected at least ", sstart,", got ", j-1))) | ||
977 | end | ||
978 | y = iterate(src, y[2]) | ||
979 | end | ||
980 | if y === nothing | ||
981 | throw(ArgumentError(LazyString( | ||
982 | "source has fewer elements than required, ", | ||
983 | "expected at least ",sstart," got ", sstart-1))) | ||
984 | end | ||
985 | i = Int(dstart) | ||
986 | while y !== nothing | ||
987 | val, st = y | ||
988 | dest[i] = val | ||
989 | i += 1 | ||
990 | y = iterate(src, st) | ||
991 | end | ||
992 | return dest | ||
993 | end | ||
994 | |||
995 | # this method must be separate from the above since src might not have a length | ||
996 | function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::Integer) | ||
997 | n < 0 && throw(ArgumentError(LazyString("tried to copy n=",n, | ||
998 | ", elements, but n should be nonnegative"))) | ||
999 | n == 0 && return dest | ||
1000 | dmax = dstart + n - 1 | ||
1001 | inds = LinearIndices(dest) | ||
1002 | if (dstart ∉ inds || dmax ∉ inds) | (sstart < 1) | ||
1003 | sstart < 1 && throw(ArgumentError(LazyString("source start offset (", | ||
1004 | sstart,") is < 1"))) | ||
1005 | throw(BoundsError(dest, dstart:dmax)) | ||
1006 | end | ||
1007 | y = iterate(src) | ||
1008 | for j = 1:(sstart-1) | ||
1009 | if y === nothing | ||
1010 | throw(ArgumentError(LazyString( | ||
1011 | "source has fewer elements than required, ", | ||
1012 | "expected at least ",sstart,", got ",j-1))) | ||
1013 | end | ||
1014 | y = iterate(src, y[2]) | ||
1015 | end | ||
1016 | i = Int(dstart) | ||
1017 | while i <= dmax && y !== nothing | ||
1018 | val, st = y | ||
1019 | @inbounds dest[i] = val | ||
1020 | y = iterate(src, st) | ||
1021 | i += 1 | ||
1022 | end | ||
1023 | i <= dmax && throw(BoundsError(dest, i)) | ||
1024 | return dest | ||
1025 | end | ||
1026 | |||
1027 | ## copy between abstract arrays - generally more efficient | ||
1028 | ## since a single index variable can be used. | ||
1029 | |||
1030 | """ | ||
1031 | copyto!(dest::AbstractArray, src) -> dest | ||
1032 | |||
1033 | Copy all elements from collection `src` to array `dest`, whose length must be greater than | ||
1034 | or equal to the length `n` of `src`. The first `n` elements of `dest` are overwritten, | ||
1035 | the other elements are left untouched. | ||
1036 | |||
1037 | See also [`copy!`](@ref Base.copy!), [`copy`](@ref). | ||
1038 | |||
1039 | # Examples | ||
1040 | ```jldoctest | ||
1041 | julia> x = [1., 0., 3., 0., 5.]; | ||
1042 | |||
1043 | julia> y = zeros(7); | ||
1044 | |||
1045 | julia> copyto!(y, x); | ||
1046 | |||
1047 | julia> y | ||
1048 | 7-element Vector{Float64}: | ||
1049 | 1.0 | ||
1050 | 0.0 | ||
1051 | 3.0 | ||
1052 | 0.0 | ||
1053 | 5.0 | ||
1054 | 0.0 | ||
1055 | 0.0 | ||
1056 | ``` | ||
1057 | """ | ||
1058 | function copyto!(dest::AbstractArray, src::AbstractArray) | ||
1059 | isempty(src) && return dest | ||
1060 | if dest isa BitArray | ||
1061 | # avoid ambiguities with other copyto!(::AbstractArray, ::SourceArray) methods | ||
1062 | return _copyto_bitarray!(dest, src) | ||
1063 | end | ||
1064 | src′ = unalias(dest, src) | ||
1065 | 28 (10 %) |
28 (100 %)
samples spent calling
copyto_unaliased!
copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(src′), src′)
|
|
1066 | end | ||
1067 | |||
1068 | function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) | ||
1069 | isempty(src) && return dest | ||
1070 | src′ = unalias(dest, src) | ||
1071 | copyto_unaliased!(deststyle, dest, srcstyle, src′) | ||
1072 | end | ||
1073 | |||
1074 | function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) | ||
1075 | isempty(src) && return dest | ||
1076 | destinds, srcinds = LinearIndices(dest), LinearIndices(src) | ||
1077 | idf, isf = first(destinds), first(srcinds) | ||
1078 | Δi = idf - isf | ||
1079 | (checkbounds(Bool, destinds, isf+Δi) & checkbounds(Bool, destinds, last(srcinds)+Δi)) || | ||
1080 | throw(BoundsError(dest, srcinds)) | ||
1081 | if deststyle isa IndexLinear | ||
1082 | if srcstyle isa IndexLinear | ||
1083 | # Single-index implementation | ||
1084 | @inbounds for i in srcinds | ||
1085 | dest[i + Δi] = src[i] | ||
1086 | end | ||
1087 | else | ||
1088 | # Dual-index implementation | ||
1089 | i = idf - 1 | ||
1090 | 16 (6 %) |
16 (100 %)
samples spent calling
iterate
@inbounds for a in src
|
|
1091 | 9 (3 %) |
9 (100 %)
samples spent calling
setindex!
dest[i+=1] = a
|
|
1092 | 3 (1 %) |
3 (100 %)
samples spent calling
iterate
end
|
|
1093 | end | ||
1094 | else | ||
1095 | iterdest, itersrc = eachindex(dest), eachindex(src) | ||
1096 | if iterdest == itersrc | ||
1097 | # Shared-iterator implementation | ||
1098 | for I in iterdest | ||
1099 | @inbounds dest[I] = src[I] | ||
1100 | end | ||
1101 | else | ||
1102 | # Dual-iterator implementation | ||
1103 | ret = iterate(iterdest) | ||
1104 | @inbounds for a in src | ||
1105 | idx, state = ret::NTuple{2,Any} | ||
1106 | dest[idx] = a | ||
1107 | ret = iterate(iterdest, state) | ||
1108 | end | ||
1109 | end | ||
1110 | end | ||
1111 | return dest | ||
1112 | end | ||
1113 | |||
1114 | function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray) | ||
1115 | copyto!(dest, dstart, src, first(LinearIndices(src)), length(src)) | ||
1116 | end | ||
1117 | |||
1118 | function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer) | ||
1119 | srcinds = LinearIndices(src) | ||
1120 | checkbounds(Bool, srcinds, sstart) || throw(BoundsError(src, sstart)) | ||
1121 | copyto!(dest, dstart, src, sstart, last(srcinds)-sstart+1) | ||
1122 | end | ||
1123 | |||
1124 | function copyto!(dest::AbstractArray, dstart::Integer, | ||
1125 | src::AbstractArray, sstart::Integer, | ||
1126 | n::Integer) | ||
1127 | n == 0 && return dest | ||
1128 | n < 0 && throw(ArgumentError(LazyString("tried to copy n=", | ||
1129 | n," elements, but n should be nonnegative"))) | ||
1130 | destinds, srcinds = LinearIndices(dest), LinearIndices(src) | ||
1131 | (checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1)) | ||
1132 | (checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1)) | ||
1133 | src′ = unalias(dest, src) | ||
1134 | @inbounds for i = 0:n-1 | ||
1135 | dest[dstart+i] = src′[sstart+i] | ||
1136 | end | ||
1137 | return dest | ||
1138 | end | ||
1139 | |||
1140 | function copy(a::AbstractArray) | ||
1141 | @_propagate_inbounds_meta | ||
1142 | copymutable(a) | ||
1143 | end | ||
1144 | |||
1145 | function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, | ||
1146 | A::AbstractVecOrMat{S}, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) where {R,S} | ||
1147 | if length(ir_dest) != length(ir_src) | ||
1148 | throw(ArgumentError(LazyString("source and destination must have same size (got ", | ||
1149 | length(ir_src)," and ",length(ir_dest),")"))) | ||
1150 | end | ||
1151 | if length(jr_dest) != length(jr_src) | ||
1152 | throw(ArgumentError(LazyString("source and destination must have same size (got ", | ||
1153 | length(jr_src)," and ",length(jr_dest),")"))) | ||
1154 | end | ||
1155 | @boundscheck checkbounds(B, ir_dest, jr_dest) | ||
1156 | @boundscheck checkbounds(A, ir_src, jr_src) | ||
1157 | A′ = unalias(B, A) | ||
1158 | jdest = first(jr_dest) | ||
1159 | for jsrc in jr_src | ||
1160 | idest = first(ir_dest) | ||
1161 | for isrc in ir_src | ||
1162 | @inbounds B[idest,jdest] = A′[isrc,jsrc] | ||
1163 | idest += step(ir_dest) | ||
1164 | end | ||
1165 | jdest += step(jr_dest) | ||
1166 | end | ||
1167 | return B | ||
1168 | end | ||
1169 | |||
1170 | @noinline _checkaxs(axd, axs) = axd == axs || throw(DimensionMismatch("axes must agree, got $axd and $axs")) | ||
1171 | |||
1172 | function copyto_axcheck!(dest, src) | ||
1173 | _checkaxs(axes(dest), axes(src)) | ||
1174 | copyto!(dest, src) | ||
1175 | end | ||
1176 | |||
1177 | """ | ||
1178 | copymutable(a) | ||
1179 | |||
1180 | Make a mutable copy of an array or iterable `a`. For `a::Array`, | ||
1181 | this is equivalent to `copy(a)`, but for other array types it may | ||
1182 | differ depending on the type of `similar(a)`. For generic iterables | ||
1183 | this is equivalent to `collect(a)`. | ||
1184 | |||
1185 | # Examples | ||
1186 | ```jldoctest | ||
1187 | julia> tup = (1, 2, 3) | ||
1188 | (1, 2, 3) | ||
1189 | |||
1190 | julia> Base.copymutable(tup) | ||
1191 | 3-element Vector{Int64}: | ||
1192 | 1 | ||
1193 | 2 | ||
1194 | 3 | ||
1195 | ``` | ||
1196 | """ | ||
1197 | function copymutable(a::AbstractArray) | ||
1198 | @_propagate_inbounds_meta | ||
1199 | copyto!(similar(a), a) | ||
1200 | end | ||
1201 | copymutable(itr) = collect(itr) | ||
1202 | |||
1203 | zero(x::AbstractArray{T}) where {T} = fill!(similar(x, typeof(zero(T))), zero(T)) | ||
1204 | |||
1205 | ## iteration support for arrays by iterating over `eachindex` in the array ## | ||
1206 | # Allows fast iteration by default for both IndexLinear and IndexCartesian arrays | ||
1207 | |||
1208 | # While the definitions for IndexLinear are all simple enough to inline on their | ||
1209 | # own, IndexCartesian's CartesianIndices is more complicated and requires explicit | ||
1210 | # inlining. | ||
1211 | function iterate(A::AbstractArray, state=(eachindex(A),)) | ||
1212 | 35 (12 %) |
35 (12 %)
samples spent in iterate
16 (46 %) (incl.) when called from iterate line 1212 16 (46 %) (incl.) when called from copyto_unaliased! line 1090 3 (9 %) (incl.) when called from copyto_unaliased! line 1092
16 (46 %)
samples spent calling
iterate
y = iterate(state...)
16 (46 %) samples spent calling iterate 3 (9 %) samples spent calling iterate |
|
1213 | y === nothing && return nothing | ||
1214 | A[y[1]], (state[1], tail(y)...) | ||
1215 | end | ||
1216 | |||
1217 | isempty(a::AbstractArray) = (length(a) == 0) | ||
1218 | |||
1219 | |||
1220 | ## range conversions ## | ||
1221 | |||
1222 | map(::Type{T}, r::StepRange) where {T<:Real} = T(r.start):T(r.step):T(last(r)) | ||
1223 | map(::Type{T}, r::UnitRange) where {T<:Real} = T(r.start):T(last(r)) | ||
1224 | map(::Type{T}, r::StepRangeLen) where {T<:AbstractFloat} = convert(StepRangeLen{T}, r) | ||
1225 | function map(::Type{T}, r::LinRange) where T<:AbstractFloat | ||
1226 | LinRange(T(r.start), T(r.stop), length(r)) | ||
1227 | end | ||
1228 | |||
1229 | ## unsafe/pointer conversions ## | ||
1230 | |||
1231 | # note: the following type definitions don't mean any AbstractArray is convertible to | ||
1232 | # a data Ref. they just map the array element type to the pointer type for | ||
1233 | # convenience in cases that work. | ||
1234 | pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, x) | ||
1235 | function pointer(x::AbstractArray{T}, i::Integer) where T | ||
1236 | @inline | ||
1237 | unsafe_convert(Ptr{T}, x) + Int(_memory_offset(x, i))::Int | ||
1238 | end | ||
1239 | |||
1240 | # The distance from pointer(x) to the element at x[I...] in bytes | ||
1241 | _memory_offset(x::DenseArray, I::Vararg{Any,N}) where {N} = (_to_linear_index(x, I...) - first(LinearIndices(x)))*elsize(x) | ||
1242 | function _memory_offset(x::AbstractArray, I::Vararg{Any,N}) where {N} | ||
1243 | J = _to_subscript_indices(x, I...) | ||
1244 | return sum(map((i, s, o)->s*(i-o), J, strides(x), Tuple(first(CartesianIndices(x)))))*elsize(x) | ||
1245 | end | ||
1246 | |||
1247 | ## Approach: | ||
1248 | # We only define one fallback method on getindex for all argument types. | ||
1249 | # That dispatches to an (inlined) internal _getindex function, where the goal is | ||
1250 | # to transform the indices such that we can call the only getindex method that | ||
1251 | # we require the type A{T,N} <: AbstractArray{T,N} to define; either: | ||
1252 | # getindex(::A, ::Int) # if IndexStyle(A) == IndexLinear() OR | ||
1253 | # getindex(::A{T,N}, ::Vararg{Int, N}) where {T,N} # if IndexCartesian() | ||
1254 | # If the subtype hasn't defined the required method, it falls back to the | ||
1255 | # _getindex function again where an error is thrown to prevent stack overflows. | ||
1256 | """ | ||
1257 | getindex(A, inds...) | ||
1258 | |||
1259 | Return a subset of array `A` as specified by `inds`, where each `ind` may be, | ||
1260 | for example, an `Int`, an [`AbstractRange`](@ref), or a [`Vector`](@ref). | ||
1261 | See the manual section on [array indexing](@ref man-array-indexing) for details. | ||
1262 | |||
1263 | # Examples | ||
1264 | ```jldoctest | ||
1265 | julia> A = [1 2; 3 4] | ||
1266 | 2×2 Matrix{Int64}: | ||
1267 | 1 2 | ||
1268 | 3 4 | ||
1269 | |||
1270 | julia> getindex(A, 1) | ||
1271 | 1 | ||
1272 | |||
1273 | julia> getindex(A, [2, 1]) | ||
1274 | 2-element Vector{Int64}: | ||
1275 | 3 | ||
1276 | 1 | ||
1277 | |||
1278 | julia> getindex(A, 2:4) | ||
1279 | 3-element Vector{Int64}: | ||
1280 | 3 | ||
1281 | 2 | ||
1282 | 4 | ||
1283 | ``` | ||
1284 | """ | ||
1285 | function getindex(A::AbstractArray, I...) | ||
1286 | @_propagate_inbounds_meta | ||
1287 | error_if_canonical_getindex(IndexStyle(A), A, I...) | ||
1288 | _getindex(IndexStyle(A), A, to_indices(A, I)...) | ||
1289 | end | ||
1290 | # To avoid invalidations from multidimensional.jl: getindex(A::Array, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) | ||
1291 | @propagate_inbounds getindex(A::Array, i1::Integer, I::Integer...) = A[to_indices(A, (i1, I...))...] | ||
1292 | |||
1293 | function unsafe_getindex(A::AbstractArray, I...) | ||
1294 | @inline | ||
1295 | @inbounds r = getindex(A, I...) | ||
1296 | r | ||
1297 | end | ||
1298 | |||
1299 | struct CanonicalIndexError <: Exception | ||
1300 | func::String | ||
1301 | type::Any | ||
1302 | CanonicalIndexError(func::String, @nospecialize(type)) = new(func, type) | ||
1303 | end | ||
1304 | |||
1305 | error_if_canonical_getindex(::IndexLinear, A::AbstractArray, ::Int) = | ||
1306 | throw(CanonicalIndexError("getindex", typeof(A))) | ||
1307 | error_if_canonical_getindex(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int,N}) where {T,N} = | ||
1308 | throw(CanonicalIndexError("getindex", typeof(A))) | ||
1309 | error_if_canonical_getindex(::IndexStyle, ::AbstractArray, ::Any...) = nothing | ||
1310 | |||
1311 | ## Internal definitions | ||
1312 | _getindex(::IndexStyle, A::AbstractArray, I...) = | ||
1313 | error("getindex for $(typeof(A)) with types $(typeof(I)) is not supported") | ||
1314 | |||
1315 | ## IndexLinear Scalar indexing: canonical method is one Int | ||
1316 | _getindex(::IndexLinear, A::AbstractVector, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) # ambiguity resolution in case packages specialize this (to be avoided if at all possible, but see Interpolations.jl) | ||
1317 | _getindex(::IndexLinear, A::AbstractArray, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) | ||
1318 | function _getindex(::IndexLinear, A::AbstractArray, I::Vararg{Int,M}) where M | ||
1319 | @inline | ||
1320 | @boundscheck checkbounds(A, I...) # generally _to_linear_index requires bounds checking | ||
1321 | @inbounds r = getindex(A, _to_linear_index(A, I...)) | ||
1322 | r | ||
1323 | end | ||
1324 | _to_linear_index(A::AbstractArray, i::Integer) = i | ||
1325 | _to_linear_index(A::AbstractVector, i::Integer, I::Integer...) = i | ||
1326 | _to_linear_index(A::AbstractArray) = first(LinearIndices(A)) | ||
1327 | _to_linear_index(A::AbstractArray, I::Integer...) = (@inline; _sub2ind(A, I...)) | ||
1328 | |||
1329 | ## IndexCartesian Scalar indexing: Canonical method is full dimensionality of Ints | ||
1330 | function _getindex(::IndexCartesian, A::AbstractArray, I::Vararg{Int,M}) where M | ||
1331 | @inline | ||
1332 | @boundscheck checkbounds(A, I...) # generally _to_subscript_indices requires bounds checking | ||
1333 | @inbounds r = getindex(A, _to_subscript_indices(A, I...)...) | ||
1334 | r | ||
1335 | end | ||
1336 | function _getindex(::IndexCartesian, A::AbstractArray{T,N}, I::Vararg{Int, N}) where {T,N} | ||
1337 | @_propagate_inbounds_meta | ||
1338 | getindex(A, I...) | ||
1339 | end | ||
1340 | _to_subscript_indices(A::AbstractArray, i::Integer) = (@inline; _unsafe_ind2sub(A, i)) | ||
1341 | _to_subscript_indices(A::AbstractArray{T,N}) where {T,N} = (@inline; fill_to_length((), 1, Val(N))) | ||
1342 | _to_subscript_indices(A::AbstractArray{T,0}) where {T} = () | ||
1343 | _to_subscript_indices(A::AbstractArray{T,0}, i::Integer) where {T} = () | ||
1344 | _to_subscript_indices(A::AbstractArray{T,0}, I::Integer...) where {T} = () | ||
1345 | function _to_subscript_indices(A::AbstractArray{T,N}, I::Integer...) where {T,N} | ||
1346 | @inline | ||
1347 | J, Jrem = IteratorsMD.split(I, Val(N)) | ||
1348 | _to_subscript_indices(A, J, Jrem) | ||
1349 | end | ||
1350 | _to_subscript_indices(A::AbstractArray, J::Tuple, Jrem::Tuple{}) = | ||
1351 | __to_subscript_indices(A, axes(A), J, Jrem) | ||
1352 | function __to_subscript_indices(A::AbstractArray, | ||
1353 | ::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, J::Tuple, Jrem::Tuple{}) | ||
1354 | @inline | ||
1355 | (J..., map(first, tail(_remaining_size(J, axes(A))))...) | ||
1356 | end | ||
1357 | _to_subscript_indices(A, J::Tuple, Jrem::Tuple) = J # already bounds-checked, safe to drop | ||
1358 | _to_subscript_indices(A::AbstractArray{T,N}, I::Vararg{Int,N}) where {T,N} = I | ||
1359 | _remaining_size(::Tuple{Any}, t::Tuple) = t | ||
1360 | _remaining_size(h::Tuple, t::Tuple) = (@inline; _remaining_size(tail(h), tail(t))) | ||
1361 | _unsafe_ind2sub(::Tuple{}, i) = () # _ind2sub may throw(BoundsError()) in this case | ||
1362 | _unsafe_ind2sub(sz, i) = (@inline; _ind2sub(sz, i)) | ||
1363 | |||
1364 | ## Setindex! is defined similarly. We first dispatch to an internal _setindex! | ||
1365 | # function that allows dispatch on array storage | ||
1366 | |||
1367 | """ | ||
1368 | setindex!(A, X, inds...) | ||
1369 | A[inds...] = X | ||
1370 | |||
1371 | Store values from array `X` within some subset of `A` as specified by `inds`. | ||
1372 | The syntax `A[inds...] = X` is equivalent to `(setindex!(A, X, inds...); X)`. | ||
1373 | |||
1374 | $(_DOCS_ALIASING_WARNING) | ||
1375 | |||
1376 | # Examples | ||
1377 | ```jldoctest | ||
1378 | julia> A = zeros(2,2); | ||
1379 | |||
1380 | julia> setindex!(A, [10, 20], [1, 2]); | ||
1381 | |||
1382 | julia> A[[3, 4]] = [30, 40]; | ||
1383 | |||
1384 | julia> A | ||
1385 | 2×2 Matrix{Float64}: | ||
1386 | 10.0 30.0 | ||
1387 | 20.0 40.0 | ||
1388 | ``` | ||
1389 | """ | ||
1390 | function setindex!(A::AbstractArray, v, I...) | ||
1391 | @_propagate_inbounds_meta | ||
1392 | error_if_canonical_setindex(IndexStyle(A), A, I...) | ||
1393 | 24 (8 %) |
24 (100 %)
samples spent calling
_setindex!
_setindex!(IndexStyle(A), A, v, to_indices(A, I)...)
|
|
1394 | end | ||
1395 | function unsafe_setindex!(A::AbstractArray, v, I...) | ||
1396 | @inline | ||
1397 | @inbounds r = setindex!(A, v, I...) | ||
1398 | r | ||
1399 | end | ||
1400 | |||
1401 | error_if_canonical_setindex(::IndexLinear, A::AbstractArray, ::Int) = | ||
1402 | throw(CanonicalIndexError("setindex!", typeof(A))) | ||
1403 | error_if_canonical_setindex(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int,N}) where {T,N} = | ||
1404 | throw(CanonicalIndexError("setindex!", typeof(A))) | ||
1405 | error_if_canonical_setindex(::IndexStyle, ::AbstractArray, ::Any...) = nothing | ||
1406 | |||
1407 | ## Internal definitions | ||
1408 | _setindex!(::IndexStyle, A::AbstractArray, v, I...) = | ||
1409 | error("setindex! for $(typeof(A)) with types $(typeof(I)) is not supported") | ||
1410 | |||
1411 | ## IndexLinear Scalar indexing | ||
1412 | _setindex!(::IndexLinear, A::AbstractArray, v, i::Int) = (@_propagate_inbounds_meta; setindex!(A, v, i)) | ||
1413 | function _setindex!(::IndexLinear, A::AbstractArray, v, I::Vararg{Int,M}) where M | ||
1414 | @inline | ||
1415 | @boundscheck checkbounds(A, I...) | ||
1416 | 24 (8 %) |
24 (100 %)
samples spent calling
setindex!
@inbounds r = setindex!(A, v, _to_linear_index(A, I...))
|
|
1417 | r | ||
1418 | end | ||
1419 | |||
1420 | # IndexCartesian Scalar indexing | ||
1421 | function _setindex!(::IndexCartesian, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) where {T,N} | ||
1422 | @_propagate_inbounds_meta | ||
1423 | setindex!(A, v, I...) | ||
1424 | end | ||
1425 | function _setindex!(::IndexCartesian, A::AbstractArray, v, I::Vararg{Int,M}) where M | ||
1426 | @inline | ||
1427 | @boundscheck checkbounds(A, I...) | ||
1428 | @inbounds r = setindex!(A, v, _to_subscript_indices(A, I...)...) | ||
1429 | r | ||
1430 | end | ||
1431 | |||
1432 | """ | ||
1433 | parent(A) | ||
1434 | |||
1435 | Return the underlying parent object of the view. This parent of objects of types `SubArray`, `SubString`, `ReshapedArray` | ||
1436 | or `LinearAlgebra.Transpose` is what was passed as an argument to `view`, `reshape`, `transpose`, etc. | ||
1437 | during object creation. If the input is not a wrapped object, return the input itself. If the input is | ||
1438 | wrapped multiple times, only the outermost wrapper will be removed. | ||
1439 | |||
1440 | # Examples | ||
1441 | ```jldoctest | ||
1442 | julia> A = [1 2; 3 4] | ||
1443 | 2×2 Matrix{Int64}: | ||
1444 | 1 2 | ||
1445 | 3 4 | ||
1446 | |||
1447 | julia> V = view(A, 1:2, :) | ||
1448 | 2×2 view(::Matrix{Int64}, 1:2, :) with eltype Int64: | ||
1449 | 1 2 | ||
1450 | 3 4 | ||
1451 | |||
1452 | julia> parent(V) | ||
1453 | 2×2 Matrix{Int64}: | ||
1454 | 1 2 | ||
1455 | 3 4 | ||
1456 | ``` | ||
1457 | """ | ||
1458 | function parent end | ||
1459 | |||
1460 | parent(a::AbstractArray) = a | ||
1461 | |||
1462 | ## rudimentary aliasing detection ## | ||
1463 | """ | ||
1464 | Base.unalias(dest, A) | ||
1465 | |||
1466 | Return either `A` or a copy of `A` in a rough effort to prevent modifications to `dest` from | ||
1467 | affecting the returned object. No guarantees are provided. | ||
1468 | |||
1469 | Custom arrays that wrap or use fields containing arrays that might alias against other | ||
1470 | external objects should provide a [`Base.dataids`](@ref) implementation. | ||
1471 | |||
1472 | This function must return an object of exactly the same type as `A` for performance and type | ||
1473 | stability. Mutable custom arrays for which [`copy(A)`](@ref) is not `typeof(A)` should | ||
1474 | provide a [`Base.unaliascopy`](@ref) implementation. | ||
1475 | |||
1476 | See also [`Base.mightalias`](@ref). | ||
1477 | """ | ||
1478 | unalias(dest, A::AbstractArray) = mightalias(dest, A) ? unaliascopy(A) : A | ||
1479 | unalias(dest, A::AbstractRange) = A | ||
1480 | unalias(dest, A) = A | ||
1481 | |||
1482 | """ | ||
1483 | Base.unaliascopy(A) | ||
1484 | |||
1485 | Make a preventative copy of `A` in an operation where `A` [`Base.mightalias`](@ref) against | ||
1486 | another array in order to preserve consistent semantics as that other array is mutated. | ||
1487 | |||
1488 | This must return an object of the same type as `A` to preserve optimal performance in the | ||
1489 | much more common case where aliasing does not occur. By default, | ||
1490 | `unaliascopy(A::AbstractArray)` will attempt to use [`copy(A)`](@ref), but in cases where | ||
1491 | `copy(A)` is not a `typeof(A)`, then the array should provide a custom implementation of | ||
1492 | `Base.unaliascopy(A)`. | ||
1493 | """ | ||
1494 | unaliascopy(A::Array) = copy(A) | ||
1495 | unaliascopy(A::AbstractArray)::typeof(A) = (@noinline; _unaliascopy(A, copy(A))) | ||
1496 | _unaliascopy(A::T, C::T) where {T} = C | ||
1497 | _unaliascopy(A, C) = throw(ArgumentError(""" | ||
1498 | an array of type `$(typename(typeof(A)).wrapper)` shares memory with another argument | ||
1499 | and must make a preventative copy of itself in order to maintain consistent semantics, | ||
1500 | but `copy(::$(typeof(A)))` returns a new array of type `$(typeof(C))`. | ||
1501 | To fix, implement: | ||
1502 | `Base.unaliascopy(A::$(typename(typeof(A)).wrapper))::typeof(A)`""")) | ||
1503 | unaliascopy(A) = A | ||
1504 | |||
1505 | """ | ||
1506 | Base.mightalias(A::AbstractArray, B::AbstractArray) | ||
1507 | |||
1508 | Perform a conservative test to check if arrays `A` and `B` might share the same memory. | ||
1509 | |||
1510 | By default, this simply checks if either of the arrays reference the same memory | ||
1511 | regions, as identified by their [`Base.dataids`](@ref). | ||
1512 | """ | ||
1513 | mightalias(A::AbstractArray, B::AbstractArray) = !isbits(A) && !isbits(B) && !_isdisjoint(dataids(A), dataids(B)) | ||
1514 | mightalias(x, y) = false | ||
1515 | |||
1516 | _isdisjoint(as::Tuple{}, bs::Tuple{}) = true | ||
1517 | _isdisjoint(as::Tuple{}, bs::Tuple{UInt}) = true | ||
1518 | _isdisjoint(as::Tuple{}, bs::Tuple) = true | ||
1519 | _isdisjoint(as::Tuple{UInt}, bs::Tuple{}) = true | ||
1520 | _isdisjoint(as::Tuple{UInt}, bs::Tuple{UInt}) = as[1] != bs[1] | ||
1521 | _isdisjoint(as::Tuple{UInt}, bs::Tuple) = !(as[1] in bs) | ||
1522 | _isdisjoint(as::Tuple, bs::Tuple{}) = true | ||
1523 | _isdisjoint(as::Tuple, bs::Tuple{UInt}) = !(bs[1] in as) | ||
1524 | _isdisjoint(as::Tuple, bs::Tuple) = !(as[1] in bs) && _isdisjoint(tail(as), bs) | ||
1525 | |||
1526 | """ | ||
1527 | Base.dataids(A::AbstractArray) | ||
1528 | |||
1529 | Return a tuple of `UInt`s that represent the mutable data segments of an array. | ||
1530 | |||
1531 | Custom arrays that would like to opt-in to aliasing detection of their component | ||
1532 | parts can specialize this method to return the concatenation of the `dataids` of | ||
1533 | their component parts. A typical definition for an array that wraps a parent is | ||
1534 | `Base.dataids(C::CustomArray) = dataids(C.parent)`. | ||
1535 | """ | ||
1536 | dataids(A::AbstractArray) = (UInt(objectid(A)),) | ||
1537 | dataids(A::Array) = (UInt(pointer(A)),) | ||
1538 | dataids(::AbstractRange) = () | ||
1539 | dataids(x) = () | ||
1540 | |||
1541 | ## get (getindex with a default value) ## | ||
1542 | |||
1543 | RangeVecIntList{A<:AbstractVector{Int}} = Union{Tuple{Vararg{Union{AbstractRange, AbstractVector{Int}}}}, | ||
1544 | AbstractVector{UnitRange{Int}}, AbstractVector{AbstractRange{Int}}, AbstractVector{A}} | ||
1545 | |||
1546 | get(A::AbstractArray, i::Integer, default) = checkbounds(Bool, A, i) ? A[i] : default | ||
1547 | get(A::AbstractArray, I::Tuple{}, default) = checkbounds(Bool, A) ? A[] : default | ||
1548 | get(A::AbstractArray, I::Dims, default) = checkbounds(Bool, A, I...) ? A[I...] : default | ||
1549 | get(f::Callable, A::AbstractArray, i::Integer) = checkbounds(Bool, A, i) ? A[i] : f() | ||
1550 | get(f::Callable, A::AbstractArray, I::Tuple{}) = checkbounds(Bool, A) ? A[] : f() | ||
1551 | get(f::Callable, A::AbstractArray, I::Dims) = checkbounds(Bool, A, I...) ? A[I...] : f() | ||
1552 | |||
1553 | function get!(X::AbstractVector{T}, A::AbstractVector, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T | ||
1554 | # 1d is not linear indexing | ||
1555 | ind = findall(in(axes1(A)), I) | ||
1556 | X[ind] = A[I[ind]] | ||
1557 | Xind = axes1(X) | ||
1558 | X[first(Xind):first(ind)-1] = default | ||
1559 | X[last(ind)+1:last(Xind)] = default | ||
1560 | X | ||
1561 | end | ||
1562 | function get!(X::AbstractArray{T}, A::AbstractArray, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T | ||
1563 | # Linear indexing | ||
1564 | ind = findall(in(1:length(A)), I) | ||
1565 | X[ind] = A[I[ind]] | ||
1566 | fill!(view(X, 1:first(ind)-1), default) | ||
1567 | fill!(view(X, last(ind)+1:length(X)), default) | ||
1568 | X | ||
1569 | end | ||
1570 | |||
1571 | get(A::AbstractArray, I::AbstractRange, default) = get!(similar(A, typeof(default), index_shape(I)), A, I, default) | ||
1572 | |||
1573 | function get!(X::AbstractArray{T}, A::AbstractArray, I::RangeVecIntList, default::T) where T | ||
1574 | fill!(X, default) | ||
1575 | dst, src = indcopy(size(A), I) | ||
1576 | X[dst...] = A[src...] | ||
1577 | X | ||
1578 | end | ||
1579 | |||
1580 | get(A::AbstractArray, I::RangeVecIntList, default) = | ||
1581 | get!(similar(A, typeof(default), index_shape(I...)), A, I, default) | ||
1582 | |||
1583 | ## structured matrix methods ## | ||
1584 | replace_in_print_matrix(A::AbstractMatrix,i::Integer,j::Integer,s::AbstractString) = s | ||
1585 | replace_in_print_matrix(A::AbstractVector,i::Integer,j::Integer,s::AbstractString) = s | ||
1586 | |||
1587 | ## Concatenation ## | ||
1588 | eltypeof(x) = typeof(x) | ||
1589 | eltypeof(x::AbstractArray) = eltype(x) | ||
1590 | |||
1591 | promote_eltypeof() = error() | ||
1592 | promote_eltypeof(v1) = eltypeof(v1) | ||
1593 | promote_eltypeof(v1, vs...) = promote_type(eltypeof(v1), promote_eltypeof(vs...)) | ||
1594 | promote_eltypeof(v1::T, vs::T...) where {T} = eltypeof(v1) | ||
1595 | promote_eltypeof(v1::AbstractArray{T}, vs::AbstractArray{T}...) where {T} = T | ||
1596 | |||
1597 | promote_eltype() = error() | ||
1598 | promote_eltype(v1) = eltype(v1) | ||
1599 | promote_eltype(v1, vs...) = promote_type(eltype(v1), promote_eltype(vs...)) | ||
1600 | promote_eltype(v1::T, vs::T...) where {T} = eltype(T) | ||
1601 | promote_eltype(v1::AbstractArray{T}, vs::AbstractArray{T}...) where {T} = T | ||
1602 | |||
1603 | #TODO: ERROR CHECK | ||
1604 | _cat(catdim::Int) = Vector{Any}() | ||
1605 | |||
1606 | typed_vcat(::Type{T}) where {T} = Vector{T}() | ||
1607 | typed_hcat(::Type{T}) where {T} = Vector{T}() | ||
1608 | |||
1609 | ## cat: special cases | ||
1610 | vcat(X::T...) where {T} = T[ X[i] for i=1:length(X) ] | ||
1611 | vcat(X::T...) where {T<:Number} = T[ X[i] for i=1:length(X) ] | ||
1612 | hcat(X::T...) where {T} = T[ X[j] for i=1:1, j=1:length(X) ] | ||
1613 | hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=1:length(X) ] | ||
1614 | |||
1615 | vcat(X::Number...) = hvcat_fill!(Vector{promote_typeof(X...)}(undef, length(X)), X) | ||
1616 | hcat(X::Number...) = hvcat_fill!(Matrix{promote_typeof(X...)}(undef, 1,length(X)), X) | ||
1617 | typed_vcat(::Type{T}, X::Number...) where {T} = hvcat_fill!(Vector{T}(undef, length(X)), X) | ||
1618 | typed_hcat(::Type{T}, X::Number...) where {T} = hvcat_fill!(Matrix{T}(undef, 1,length(X)), X) | ||
1619 | |||
1620 | vcat(V::AbstractVector...) = typed_vcat(promote_eltype(V...), V...) | ||
1621 | vcat(V::AbstractVector{T}...) where {T} = typed_vcat(T, V...) | ||
1622 | |||
1623 | # FIXME: this alias would better be Union{AbstractVector{T}, Tuple{Vararg{T}}} | ||
1624 | # and method signatures should do AbstractVecOrTuple{<:T} when they want covariance, | ||
1625 | # but that solution currently fails (see #27188 and #27224) | ||
1626 | AbstractVecOrTuple{T} = Union{AbstractVector{<:T}, Tuple{Vararg{T}}} | ||
1627 | |||
1628 | _typed_vcat_similar(V, ::Type{T}, n) where T = similar(V[1], T, n) | ||
1629 | _typed_vcat(::Type{T}, V::AbstractVecOrTuple{AbstractVector}) where T = | ||
1630 | _typed_vcat!(_typed_vcat_similar(V, T, sum(map(length, V))), V) | ||
1631 | |||
1632 | function _typed_vcat!(a::AbstractVector{T}, V::AbstractVecOrTuple{AbstractVector}) where T | ||
1633 | pos = 1 | ||
1634 | for k=1:Int(length(V))::Int | ||
1635 | Vk = V[k] | ||
1636 | p1 = pos + Int(length(Vk))::Int - 1 | ||
1637 | a[pos:p1] = Vk | ||
1638 | pos = p1+1 | ||
1639 | end | ||
1640 | a | ||
1641 | end | ||
1642 | |||
1643 | typed_hcat(::Type{T}, A::AbstractVecOrMat...) where {T} = _typed_hcat(T, A) | ||
1644 | |||
1645 | # Catch indexing errors like v[i +1] (instead of v[i+1] or v[i + 1]), where indexing is | ||
1646 | # interpreted as a typed concatenation. (issue #49676) | ||
1647 | typed_hcat(::AbstractArray, other...) = throw(ArgumentError("It is unclear whether you \ | ||
1648 | intend to perform an indexing operation or typed concatenation. If you intend to \ | ||
1649 | perform indexing (v[1 + 2]), adjust spacing or insert missing operator to clarify. \ | ||
1650 | If you intend to perform typed concatenation (T[1 2]), ensure that T is a type.")) | ||
1651 | |||
1652 | |||
1653 | hcat(A::AbstractVecOrMat...) = typed_hcat(promote_eltype(A...), A...) | ||
1654 | hcat(A::AbstractVecOrMat{T}...) where {T} = typed_hcat(T, A...) | ||
1655 | |||
1656 | function _typed_hcat(::Type{T}, A::AbstractVecOrTuple{AbstractVecOrMat}) where T | ||
1657 | nargs = length(A) | ||
1658 | nrows = size(A[1], 1) | ||
1659 | ncols = 0 | ||
1660 | dense = true | ||
1661 | for j = 1:nargs | ||
1662 | Aj = A[j] | ||
1663 | if size(Aj, 1) != nrows | ||
1664 | throw(DimensionMismatch("number of rows of each array must match (got $(map(x->size(x,1), A)))")) | ||
1665 | end | ||
1666 | dense &= isa(Aj,Array) | ||
1667 | nd = ndims(Aj) | ||
1668 | ncols += (nd==2 ? size(Aj,2) : 1) | ||
1669 | end | ||
1670 | B = similar(A[1], T, nrows, ncols) | ||
1671 | pos = 1 | ||
1672 | if dense | ||
1673 | for k=1:nargs | ||
1674 | Ak = A[k] | ||
1675 | n = length(Ak) | ||
1676 | copyto!(B, pos, Ak, 1, n) | ||
1677 | pos += n | ||
1678 | end | ||
1679 | else | ||
1680 | for k=1:nargs | ||
1681 | Ak = A[k] | ||
1682 | p1 = pos+(isa(Ak,AbstractMatrix) ? size(Ak, 2) : 1)-1 | ||
1683 | B[:, pos:p1] = Ak | ||
1684 | pos = p1+1 | ||
1685 | end | ||
1686 | end | ||
1687 | return B | ||
1688 | end | ||
1689 | |||
1690 | vcat(A::AbstractVecOrMat...) = typed_vcat(promote_eltype(A...), A...) | ||
1691 | vcat(A::AbstractVecOrMat{T}...) where {T} = typed_vcat(T, A...) | ||
1692 | |||
1693 | function _typed_vcat(::Type{T}, A::AbstractVecOrTuple{AbstractVecOrMat}) where T | ||
1694 | nargs = length(A) | ||
1695 | nrows = sum(a->size(a, 1), A)::Int | ||
1696 | ncols = size(A[1], 2) | ||
1697 | for j = 2:nargs | ||
1698 | if size(A[j], 2) != ncols | ||
1699 | throw(DimensionMismatch("number of columns of each array must match (got $(map(x->size(x,2), A)))")) | ||
1700 | end | ||
1701 | end | ||
1702 | B = similar(A[1], T, nrows, ncols) | ||
1703 | pos = 1 | ||
1704 | for k=1:nargs | ||
1705 | Ak = A[k] | ||
1706 | p1 = pos+size(Ak,1)::Int-1 | ||
1707 | B[pos:p1, :] = Ak | ||
1708 | pos = p1+1 | ||
1709 | end | ||
1710 | return B | ||
1711 | end | ||
1712 | |||
1713 | typed_vcat(::Type{T}, A::AbstractVecOrMat...) where {T} = _typed_vcat(T, A) | ||
1714 | |||
1715 | reduce(::typeof(vcat), A::AbstractVector{<:AbstractVecOrMat}) = | ||
1716 | _typed_vcat(mapreduce(eltype, promote_type, A), A) | ||
1717 | |||
1718 | reduce(::typeof(hcat), A::AbstractVector{<:AbstractVecOrMat}) = | ||
1719 | _typed_hcat(mapreduce(eltype, promote_type, A), A) | ||
1720 | |||
1721 | ## cat: general case | ||
1722 | |||
1723 | # helper functions | ||
1724 | cat_size(A) = (1,) | ||
1725 | cat_size(A::AbstractArray) = size(A) | ||
1726 | cat_size(A, d) = 1 | ||
1727 | cat_size(A::AbstractArray, d) = size(A, d) | ||
1728 | |||
1729 | cat_length(::Any) = 1 | ||
1730 | cat_length(a::AbstractArray) = length(a) | ||
1731 | |||
1732 | cat_ndims(a) = 0 | ||
1733 | cat_ndims(a::AbstractArray) = ndims(a) | ||
1734 | |||
1735 | cat_indices(A, d) = OneTo(1) | ||
1736 | cat_indices(A::AbstractArray, d) = axes(A, d) | ||
1737 | |||
1738 | cat_similar(A, ::Type{T}, shape::Tuple) where T = Array{T}(undef, shape) | ||
1739 | cat_similar(A, ::Type{T}, shape::Vector) where T = Array{T}(undef, shape...) | ||
1740 | cat_similar(A::Array, ::Type{T}, shape::Tuple) where T = Array{T}(undef, shape) | ||
1741 | cat_similar(A::Array, ::Type{T}, shape::Vector) where T = Array{T}(undef, shape...) | ||
1742 | cat_similar(A::AbstractArray, T::Type, shape::Tuple) = similar(A, T, shape) | ||
1743 | cat_similar(A::AbstractArray, T::Type, shape::Vector) = similar(A, T, shape...) | ||
1744 | |||
1745 | # These are for backwards compatibility (even though internal) | ||
1746 | cat_shape(dims, shape::Tuple{Vararg{Int}}) = shape | ||
1747 | function cat_shape(dims, shapes::Tuple) | ||
1748 | out_shape = () | ||
1749 | for s in shapes | ||
1750 | out_shape = _cshp(1, dims, out_shape, s) | ||
1751 | end | ||
1752 | return out_shape | ||
1753 | end | ||
1754 | # The new way to compute the shape (more inferable than combining cat_size & cat_shape, due to Varargs + issue#36454) | ||
1755 | cat_size_shape(dims) = ntuple(zero, Val(length(dims))) | ||
1756 | @inline cat_size_shape(dims, X, tail...) = _cat_size_shape(dims, _cshp(1, dims, (), cat_size(X)), tail...) | ||
1757 | _cat_size_shape(dims, shape) = shape | ||
1758 | @inline _cat_size_shape(dims, shape, X, tail...) = _cat_size_shape(dims, _cshp(1, dims, shape, cat_size(X)), tail...) | ||
1759 | |||
1760 | _cshp(ndim::Int, ::Tuple{}, ::Tuple{}, ::Tuple{}) = () | ||
1761 | _cshp(ndim::Int, ::Tuple{}, ::Tuple{}, nshape) = nshape | ||
1762 | _cshp(ndim::Int, dims, ::Tuple{}, ::Tuple{}) = ntuple(Returns(1), Val(length(dims))) | ||
1763 | @inline _cshp(ndim::Int, dims, shape, ::Tuple{}) = | ||
1764 | (shape[1] + dims[1], _cshp(ndim + 1, tail(dims), tail(shape), ())...) | ||
1765 | @inline _cshp(ndim::Int, dims, ::Tuple{}, nshape) = | ||
1766 | (nshape[1], _cshp(ndim + 1, tail(dims), (), tail(nshape))...) | ||
1767 | @inline function _cshp(ndim::Int, ::Tuple{}, shape, ::Tuple{}) | ||
1768 | _cs(ndim, shape[1], 1) | ||
1769 | (1, _cshp(ndim + 1, (), tail(shape), ())...) | ||
1770 | end | ||
1771 | @inline function _cshp(ndim::Int, ::Tuple{}, shape, nshape) | ||
1772 | next = _cs(ndim, shape[1], nshape[1]) | ||
1773 | (next, _cshp(ndim + 1, (), tail(shape), tail(nshape))...) | ||
1774 | end | ||
1775 | @inline function _cshp(ndim::Int, dims, shape, nshape) | ||
1776 | a = shape[1] | ||
1777 | b = nshape[1] | ||
1778 | next = dims[1] ? a + b : _cs(ndim, a, b) | ||
1779 | (next, _cshp(ndim + 1, tail(dims), tail(shape), tail(nshape))...) | ||
1780 | end | ||
1781 | |||
1782 | _cs(d, a, b) = (a == b ? a : throw(DimensionMismatch( | ||
1783 | "mismatch in dimension $d (expected $a got $b)"))) | ||
1784 | |||
1785 | dims2cat(::Val{dims}) where dims = dims2cat(dims) | ||
1786 | function dims2cat(dims) | ||
1787 | if any(≤(0), dims) | ||
1788 | throw(ArgumentError("All cat dimensions must be positive integers, but got $dims")) | ||
1789 | end | ||
1790 | ntuple(in(dims), maximum(dims)) | ||
1791 | end | ||
1792 | |||
1793 | _cat(dims, X...) = _cat_t(dims, promote_eltypeof(X...), X...) | ||
1794 | |||
1795 | @inline function _cat_t(dims, ::Type{T}, X...) where {T} | ||
1796 | catdims = dims2cat(dims) | ||
1797 | shape = cat_size_shape(catdims, X...) | ||
1798 | A = cat_similar(X[1], T, shape) | ||
1799 | if count(!iszero, catdims)::Int > 1 | ||
1800 | fill!(A, zero(T)) | ||
1801 | end | ||
1802 | return __cat(A, shape, catdims, X...) | ||
1803 | end | ||
1804 | # this version of `cat_t` is not very kind for inference and so its usage should be avoided, | ||
1805 | # nevertheless it is here just for compat after https://github.com/JuliaLang/julia/pull/45028 | ||
1806 | @inline cat_t(::Type{T}, X...; dims) where {T} = _cat_t(dims, T, X...) | ||
1807 | |||
1808 | # Why isn't this called `__cat!`? | ||
1809 | __cat(A, shape, catdims, X...) = __cat_offset!(A, shape, catdims, ntuple(zero, length(shape)), X...) | ||
1810 | |||
1811 | function __cat_offset!(A, shape, catdims, offsets, x, X...) | ||
1812 | # splitting the "work" on x from X... may reduce latency (fewer costly specializations) | ||
1813 | newoffsets = __cat_offset1!(A, shape, catdims, offsets, x) | ||
1814 | return __cat_offset!(A, shape, catdims, newoffsets, X...) | ||
1815 | end | ||
1816 | __cat_offset!(A, shape, catdims, offsets) = A | ||
1817 | |||
1818 | function __cat_offset1!(A, shape, catdims, offsets, x) | ||
1819 | inds = ntuple(length(offsets)) do i | ||
1820 | (i <= length(catdims) && catdims[i]) ? offsets[i] .+ cat_indices(x, i) : 1:shape[i] | ||
1821 | end | ||
1822 | _copy_or_fill!(A, inds, x) | ||
1823 | newoffsets = ntuple(length(offsets)) do i | ||
1824 | (i <= length(catdims) && catdims[i]) ? offsets[i] + cat_size(x, i) : offsets[i] | ||
1825 | end | ||
1826 | return newoffsets | ||
1827 | end | ||
1828 | |||
1829 | _copy_or_fill!(A, inds, x) = fill!(view(A, inds...), x) | ||
1830 | _copy_or_fill!(A, inds, x::AbstractArray) = (A[inds...] = x) | ||
1831 | |||
1832 | """ | ||
1833 | vcat(A...) | ||
1834 | |||
1835 | Concatenate arrays or numbers vertically. Equivalent to [`cat`](@ref)`(A...; dims=1)`, | ||
1836 | and to the syntax `[a; b; c]`. | ||
1837 | |||
1838 | To concatenate a large vector of arrays, `reduce(vcat, A)` calls an efficient method | ||
1839 | when `A isa AbstractVector{<:AbstractVecOrMat}`, rather than working pairwise. | ||
1840 | |||
1841 | See also [`hcat`](@ref), [`Iterators.flatten`](@ref), [`stack`](@ref). | ||
1842 | |||
1843 | # Examples | ||
1844 | ```jldoctest | ||
1845 | julia> v = vcat([1,2], [3,4]) | ||
1846 | 4-element Vector{Int64}: | ||
1847 | 1 | ||
1848 | 2 | ||
1849 | 3 | ||
1850 | 4 | ||
1851 | |||
1852 | julia> v == vcat(1, 2, [3,4]) # accepts numbers | ||
1853 | true | ||
1854 | |||
1855 | julia> v == [1; 2; [3,4]] # syntax for the same operation | ||
1856 | true | ||
1857 | |||
1858 | julia> summary(ComplexF64[1; 2; [3,4]]) # syntax for supplying the element type | ||
1859 | "4-element Vector{ComplexF64}" | ||
1860 | |||
1861 | julia> vcat(range(1, 2, length=3)) # collects lazy ranges | ||
1862 | 3-element Vector{Float64}: | ||
1863 | 1.0 | ||
1864 | 1.5 | ||
1865 | 2.0 | ||
1866 | |||
1867 | julia> two = ([10, 20, 30]', Float64[4 5 6; 7 8 9]) # row vector and a matrix | ||
1868 | ([10 20 30], [4.0 5.0 6.0; 7.0 8.0 9.0]) | ||
1869 | |||
1870 | julia> vcat(two...) | ||
1871 | 3×3 Matrix{Float64}: | ||
1872 | 10.0 20.0 30.0 | ||
1873 | 4.0 5.0 6.0 | ||
1874 | 7.0 8.0 9.0 | ||
1875 | |||
1876 | julia> vs = [[1, 2], [3, 4], [5, 6]]; | ||
1877 | |||
1878 | julia> reduce(vcat, vs) # more efficient than vcat(vs...) | ||
1879 | 6-element Vector{Int64}: | ||
1880 | 1 | ||
1881 | 2 | ||
1882 | 3 | ||
1883 | 4 | ||
1884 | 5 | ||
1885 | 6 | ||
1886 | |||
1887 | julia> ans == collect(Iterators.flatten(vs)) | ||
1888 | true | ||
1889 | ``` | ||
1890 | """ | ||
1891 | vcat(X...) = cat(X...; dims=Val(1)) | ||
1892 | """ | ||
1893 | hcat(A...) | ||
1894 | |||
1895 | Concatenate arrays or numbers horizontally. Equivalent to [`cat`](@ref)`(A...; dims=2)`, | ||
1896 | and to the syntax `[a b c]` or `[a;; b;; c]`. | ||
1897 | |||
1898 | For a large vector of arrays, `reduce(hcat, A)` calls an efficient method | ||
1899 | when `A isa AbstractVector{<:AbstractVecOrMat}`. | ||
1900 | For a vector of vectors, this can also be written [`stack`](@ref)`(A)`. | ||
1901 | |||
1902 | See also [`vcat`](@ref), [`hvcat`](@ref). | ||
1903 | |||
1904 | # Examples | ||
1905 | ```jldoctest | ||
1906 | julia> hcat([1,2], [3,4], [5,6]) | ||
1907 | 2×3 Matrix{Int64}: | ||
1908 | 1 3 5 | ||
1909 | 2 4 6 | ||
1910 | |||
1911 | julia> hcat(1, 2, [30 40], [5, 6, 7]') # accepts numbers | ||
1912 | 1×7 Matrix{Int64}: | ||
1913 | 1 2 30 40 5 6 7 | ||
1914 | |||
1915 | julia> ans == [1 2 [30 40] [5, 6, 7]'] # syntax for the same operation | ||
1916 | true | ||
1917 | |||
1918 | julia> Float32[1 2 [30 40] [5, 6, 7]'] # syntax for supplying the eltype | ||
1919 | 1×7 Matrix{Float32}: | ||
1920 | 1.0 2.0 30.0 40.0 5.0 6.0 7.0 | ||
1921 | |||
1922 | julia> ms = [zeros(2,2), [1 2; 3 4], [50 60; 70 80]]; | ||
1923 | |||
1924 | julia> reduce(hcat, ms) # more efficient than hcat(ms...) | ||
1925 | 2×6 Matrix{Float64}: | ||
1926 | 0.0 0.0 1.0 2.0 50.0 60.0 | ||
1927 | 0.0 0.0 3.0 4.0 70.0 80.0 | ||
1928 | |||
1929 | julia> stack(ms) |> summary # disagrees on a vector of matrices | ||
1930 | "2×2×3 Array{Float64, 3}" | ||
1931 | |||
1932 | julia> hcat(Int[], Int[], Int[]) # empty vectors, each of size (0,) | ||
1933 | 0×3 Matrix{Int64} | ||
1934 | |||
1935 | julia> hcat([1.1, 9.9], Matrix(undef, 2, 0)) # hcat with empty 2×0 Matrix | ||
1936 | 2×1 Matrix{Any}: | ||
1937 | 1.1 | ||
1938 | 9.9 | ||
1939 | ``` | ||
1940 | """ | ||
1941 | hcat(X...) = cat(X...; dims=Val(2)) | ||
1942 | |||
1943 | typed_vcat(::Type{T}, X...) where T = _cat_t(Val(1), T, X...) | ||
1944 | typed_hcat(::Type{T}, X...) where T = _cat_t(Val(2), T, X...) | ||
1945 | |||
1946 | """ | ||
1947 | cat(A...; dims) | ||
1948 | |||
1949 | Concatenate the input arrays along the dimensions specified in `dims`. | ||
1950 | |||
1951 | Along a dimension `d in dims`, the size of the output array is `sum(size(a,d) for | ||
1952 | a in A)`. | ||
1953 | Along other dimensions, all input arrays should have the same size, | ||
1954 | which will also be the size of the output array along those dimensions. | ||
1955 | |||
1956 | If `dims` is a single number, the different arrays are tightly packed along that dimension. | ||
1957 | If `dims` is an iterable containing several dimensions, the positions along these dimensions | ||
1958 | are increased simultaneously for each input array, filling with zero elsewhere. | ||
1959 | This allows one to construct block-diagonal matrices as `cat(matrices...; dims=(1,2))`, | ||
1960 | and their higher-dimensional analogues. | ||
1961 | |||
1962 | The special case `dims=1` is [`vcat`](@ref), and `dims=2` is [`hcat`](@ref). | ||
1963 | See also [`hvcat`](@ref), [`hvncat`](@ref), [`stack`](@ref), [`repeat`](@ref). | ||
1964 | |||
1965 | The keyword also accepts `Val(dims)`. | ||
1966 | |||
1967 | !!! compat "Julia 1.8" | ||
1968 | For multiple dimensions `dims = Val(::Tuple)` was added in Julia 1.8. | ||
1969 | |||
1970 | # Examples | ||
1971 | ```jldoctest | ||
1972 | julia> cat([1 2; 3 4], [pi, pi], fill(10, 2,3,1); dims=2) # same as hcat | ||
1973 | 2×6×1 Array{Float64, 3}: | ||
1974 | [:, :, 1] = | ||
1975 | 1.0 2.0 3.14159 10.0 10.0 10.0 | ||
1976 | 3.0 4.0 3.14159 10.0 10.0 10.0 | ||
1977 | |||
1978 | julia> cat(true, trues(2,2), trues(4)', dims=(1,2)) # block-diagonal | ||
1979 | 4×7 Matrix{Bool}: | ||
1980 | 1 0 0 0 0 0 0 | ||
1981 | 0 1 1 0 0 0 0 | ||
1982 | 0 1 1 0 0 0 0 | ||
1983 | 0 0 0 1 1 1 1 | ||
1984 | |||
1985 | julia> cat(1, [2], [3;;]; dims=Val(2)) | ||
1986 | 1×3 Matrix{Int64}: | ||
1987 | 1 2 3 | ||
1988 | ``` | ||
1989 | """ | ||
1990 | @inline cat(A...; dims) = _cat(dims, A...) | ||
1991 | # `@constprop :aggressive` allows `catdims` to be propagated as constant improving return type inference | ||
1992 | @constprop :aggressive _cat(catdims, A::AbstractArray{T}...) where {T} = _cat_t(catdims, T, A...) | ||
1993 | |||
1994 | # The specializations for 1 and 2 inputs are important | ||
1995 | # especially when running with --inline=no, see #11158 | ||
1996 | vcat(A::AbstractArray) = cat(A; dims=Val(1)) | ||
1997 | vcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(1)) | ||
1998 | vcat(A::AbstractArray...) = cat(A...; dims=Val(1)) | ||
1999 | vcat(A::Union{AbstractArray,Number}...) = cat(A...; dims=Val(1)) | ||
2000 | hcat(A::AbstractArray) = cat(A; dims=Val(2)) | ||
2001 | hcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(2)) | ||
2002 | hcat(A::AbstractArray...) = cat(A...; dims=Val(2)) | ||
2003 | hcat(A::Union{AbstractArray,Number}...) = cat(A...; dims=Val(2)) | ||
2004 | |||
2005 | typed_vcat(T::Type, A::AbstractArray) = _cat_t(Val(1), T, A) | ||
2006 | typed_vcat(T::Type, A::AbstractArray, B::AbstractArray) = _cat_t(Val(1), T, A, B) | ||
2007 | typed_vcat(T::Type, A::AbstractArray...) = _cat_t(Val(1), T, A...) | ||
2008 | typed_hcat(T::Type, A::AbstractArray) = _cat_t(Val(2), T, A) | ||
2009 | typed_hcat(T::Type, A::AbstractArray, B::AbstractArray) = _cat_t(Val(2), T, A, B) | ||
2010 | typed_hcat(T::Type, A::AbstractArray...) = _cat_t(Val(2), T, A...) | ||
2011 | |||
2012 | # 2d horizontal and vertical concatenation | ||
2013 | |||
2014 | # these are produced in lowering if splatting occurs inside hvcat | ||
2015 | hvcat_rows(rows::Tuple...) = hvcat(map(length, rows), (rows...)...) | ||
2016 | typed_hvcat_rows(T::Type, rows::Tuple...) = typed_hvcat(T, map(length, rows), (rows...)...) | ||
2017 | |||
2018 | function hvcat(nbc::Int, as...) | ||
2019 | # nbc = # of block columns | ||
2020 | n = length(as) | ||
2021 | mod(n,nbc) != 0 && | ||
2022 | throw(ArgumentError("number of arrays $n is not a multiple of the requested number of block columns $nbc")) | ||
2023 | nbr = div(n,nbc) | ||
2024 | hvcat(ntuple(Returns(nbc), nbr), as...) | ||
2025 | end | ||
2026 | |||
2027 | """ | ||
2028 | hvcat(blocks_per_row::Union{Tuple{Vararg{Int}}, Int}, values...) | ||
2029 | |||
2030 | Horizontal and vertical concatenation in one call. This function is called for block matrix | ||
2031 | syntax. The first argument specifies the number of arguments to concatenate in each block | ||
2032 | row. If the first argument is a single integer `n`, then all block rows are assumed to have `n` | ||
2033 | block columns. | ||
2034 | |||
2035 | # Examples | ||
2036 | ```jldoctest | ||
2037 | julia> a, b, c, d, e, f = 1, 2, 3, 4, 5, 6 | ||
2038 | (1, 2, 3, 4, 5, 6) | ||
2039 | |||
2040 | julia> [a b c; d e f] | ||
2041 | 2×3 Matrix{Int64}: | ||
2042 | 1 2 3 | ||
2043 | 4 5 6 | ||
2044 | |||
2045 | julia> hvcat((3,3), a,b,c,d,e,f) | ||
2046 | 2×3 Matrix{Int64}: | ||
2047 | 1 2 3 | ||
2048 | 4 5 6 | ||
2049 | |||
2050 | julia> [a b; c d; e f] | ||
2051 | 3×2 Matrix{Int64}: | ||
2052 | 1 2 | ||
2053 | 3 4 | ||
2054 | 5 6 | ||
2055 | |||
2056 | julia> hvcat((2,2,2), a,b,c,d,e,f) | ||
2057 | 3×2 Matrix{Int64}: | ||
2058 | 1 2 | ||
2059 | 3 4 | ||
2060 | 5 6 | ||
2061 | julia> hvcat((2,2,2), a,b,c,d,e,f) == hvcat(2, a,b,c,d,e,f) | ||
2062 | true | ||
2063 | ``` | ||
2064 | """ | ||
2065 | hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractArray...) = typed_hvcat(promote_eltype(xs...), rows, xs...) | ||
2066 | hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractArray{T}...) where {T} = typed_hvcat(T, rows, xs...) | ||
2067 | |||
2068 | function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, as::AbstractVecOrMat...) where T | ||
2069 | nbr = length(rows) # number of block rows | ||
2070 | |||
2071 | nc = 0 | ||
2072 | for i=1:rows[1] | ||
2073 | nc += size(as[i],2) | ||
2074 | end | ||
2075 | |||
2076 | nr = 0 | ||
2077 | a = 1 | ||
2078 | for i = 1:nbr | ||
2079 | nr += size(as[a],1) | ||
2080 | a += rows[i] | ||
2081 | end | ||
2082 | |||
2083 | out = similar(as[1], T, nr, nc) | ||
2084 | |||
2085 | a = 1 | ||
2086 | r = 1 | ||
2087 | for i = 1:nbr | ||
2088 | c = 1 | ||
2089 | szi = size(as[a],1) | ||
2090 | for j = 1:rows[i] | ||
2091 | Aj = as[a+j-1] | ||
2092 | szj = size(Aj,2) | ||
2093 | if size(Aj,1) != szi | ||
2094 | throw(DimensionMismatch("mismatched height in block row $(i) (expected $szi, got $(size(Aj,1)))")) | ||
2095 | end | ||
2096 | if c-1+szj > nc | ||
2097 | throw(DimensionMismatch("block row $(i) has mismatched number of columns (expected $nc, got $(c-1+szj))")) | ||
2098 | end | ||
2099 | out[r:r-1+szi, c:c-1+szj] = Aj | ||
2100 | c += szj | ||
2101 | end | ||
2102 | if c != nc+1 | ||
2103 | throw(DimensionMismatch("block row $(i) has mismatched number of columns (expected $nc, got $(c-1))")) | ||
2104 | end | ||
2105 | r += szi | ||
2106 | a += rows[i] | ||
2107 | end | ||
2108 | out | ||
2109 | end | ||
2110 | |||
2111 | hvcat(rows::Tuple{Vararg{Int}}) = [] | ||
2112 | typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}) where {T} = Vector{T}() | ||
2113 | |||
2114 | function hvcat(rows::Tuple{Vararg{Int}}, xs::T...) where T<:Number | ||
2115 | nr = length(rows) | ||
2116 | nc = rows[1] | ||
2117 | |||
2118 | a = Matrix{T}(undef, nr, nc) | ||
2119 | if length(a) != length(xs) | ||
2120 | throw(ArgumentError("argument count does not match specified shape (expected $(length(a)), got $(length(xs)))")) | ||
2121 | end | ||
2122 | k = 1 | ||
2123 | @inbounds for i=1:nr | ||
2124 | if nc != rows[i] | ||
2125 | throw(DimensionMismatch("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))")) | ||
2126 | end | ||
2127 | for j=1:nc | ||
2128 | a[i,j] = xs[k] | ||
2129 | k += 1 | ||
2130 | end | ||
2131 | end | ||
2132 | a | ||
2133 | end | ||
2134 | |||
2135 | function hvcat_fill!(a::Array, xs::Tuple) | ||
2136 | nr, nc = size(a,1), size(a,2) | ||
2137 | len = length(xs) | ||
2138 | if nr*nc != len | ||
2139 | throw(ArgumentError("argument count $(len) does not match specified shape $((nr,nc))")) | ||
2140 | end | ||
2141 | k = 1 | ||
2142 | for i=1:nr | ||
2143 | @inbounds for j=1:nc | ||
2144 | a[i,j] = xs[k] | ||
2145 | k += 1 | ||
2146 | end | ||
2147 | end | ||
2148 | a | ||
2149 | end | ||
2150 | |||
2151 | hvcat(rows::Tuple{Vararg{Int}}, xs::Number...) = typed_hvcat(promote_typeof(xs...), rows, xs...) | ||
2152 | hvcat(rows::Tuple{Vararg{Int}}, xs...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) | ||
2153 | # the following method is needed to provide a more specific one compared to LinearAlgebra/uniformscaling.jl | ||
2154 | hvcat(rows::Tuple{Vararg{Int}}, xs::Union{AbstractArray,Number}...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) | ||
2155 | |||
2156 | function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, xs::Number...) where T | ||
2157 | nr = length(rows) | ||
2158 | nc = rows[1] | ||
2159 | for i = 2:nr | ||
2160 | if nc != rows[i] | ||
2161 | throw(DimensionMismatch("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))")) | ||
2162 | end | ||
2163 | end | ||
2164 | hvcat_fill!(Matrix{T}(undef, nr, nc), xs) | ||
2165 | end | ||
2166 | |||
2167 | function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, as...) where T | ||
2168 | nbr = length(rows) # number of block rows | ||
2169 | rs = Vector{Any}(undef, nbr) | ||
2170 | a = 1 | ||
2171 | for i = 1:nbr | ||
2172 | rs[i] = typed_hcat(T, as[a:a-1+rows[i]]...) | ||
2173 | a += rows[i] | ||
2174 | end | ||
2175 | T[rs...;] | ||
2176 | end | ||
2177 | |||
2178 | ## N-dimensional concatenation ## | ||
2179 | |||
2180 | """ | ||
2181 | hvncat(dim::Int, row_first, values...) | ||
2182 | hvncat(dims::Tuple{Vararg{Int}}, row_first, values...) | ||
2183 | hvncat(shape::Tuple{Vararg{Tuple}}, row_first, values...) | ||
2184 | |||
2185 | Horizontal, vertical, and n-dimensional concatenation of many `values` in one call. | ||
2186 | |||
2187 | This function is called for block matrix syntax. The first argument either specifies the | ||
2188 | shape of the concatenation, similar to `hvcat`, as a tuple of tuples, or the dimensions that | ||
2189 | specify the key number of elements along each axis, and is used to determine the output | ||
2190 | dimensions. The `dims` form is more performant, and is used by default when the concatenation | ||
2191 | operation has the same number of elements along each axis (e.g., [a b; c d;;; e f ; g h]). | ||
2192 | The `shape` form is used when the number of elements along each axis is unbalanced | ||
2193 | (e.g., [a b ; c]). Unbalanced syntax needs additional validation overhead. The `dim` form | ||
2194 | is an optimization for concatenation along just one dimension. `row_first` indicates how | ||
2195 | `values` are ordered. The meaning of the first and second elements of `shape` are also | ||
2196 | swapped based on `row_first`. | ||
2197 | |||
2198 | # Examples | ||
2199 | ```jldoctest | ||
2200 | julia> a, b, c, d, e, f = 1, 2, 3, 4, 5, 6 | ||
2201 | (1, 2, 3, 4, 5, 6) | ||
2202 | |||
2203 | julia> [a b c;;; d e f] | ||
2204 | 1×3×2 Array{Int64, 3}: | ||
2205 | [:, :, 1] = | ||
2206 | 1 2 3 | ||
2207 | |||
2208 | [:, :, 2] = | ||
2209 | 4 5 6 | ||
2210 | |||
2211 | julia> hvncat((2,1,3), false, a,b,c,d,e,f) | ||
2212 | 2×1×3 Array{Int64, 3}: | ||
2213 | [:, :, 1] = | ||
2214 | 1 | ||
2215 | 2 | ||
2216 | |||
2217 | [:, :, 2] = | ||
2218 | 3 | ||
2219 | 4 | ||
2220 | |||
2221 | [:, :, 3] = | ||
2222 | 5 | ||
2223 | 6 | ||
2224 | |||
2225 | julia> [a b;;; c d;;; e f] | ||
2226 | 1×2×3 Array{Int64, 3}: | ||
2227 | [:, :, 1] = | ||
2228 | 1 2 | ||
2229 | |||
2230 | [:, :, 2] = | ||
2231 | 3 4 | ||
2232 | |||
2233 | [:, :, 3] = | ||
2234 | 5 6 | ||
2235 | |||
2236 | julia> hvncat(((3, 3), (3, 3), (6,)), true, a, b, c, d, e, f) | ||
2237 | 1×3×2 Array{Int64, 3}: | ||
2238 | [:, :, 1] = | ||
2239 | 1 2 3 | ||
2240 | |||
2241 | [:, :, 2] = | ||
2242 | 4 5 6 | ||
2243 | ``` | ||
2244 | |||
2245 | # Examples for construction of the arguments | ||
2246 | ``` | ||
2247 | [a b c ; d e f ;;; | ||
2248 | g h i ; j k l ;;; | ||
2249 | m n o ; p q r ;;; | ||
2250 | s t u ; v w x] | ||
2251 | ⇒ dims = (2, 3, 4) | ||
2252 | |||
2253 | [a b ; c ;;; d ;;;;] | ||
2254 | ___ _ _ | ||
2255 | 2 1 1 = elements in each row (2, 1, 1) | ||
2256 | _______ _ | ||
2257 | 3 1 = elements in each column (3, 1) | ||
2258 | _____________ | ||
2259 | 4 = elements in each 3d slice (4,) | ||
2260 | _____________ | ||
2261 | 4 = elements in each 4d slice (4,) | ||
2262 | ⇒ shape = ((2, 1, 1), (3, 1), (4,), (4,)) with `row_first` = true | ||
2263 | ``` | ||
2264 | """ | ||
2265 | hvncat(dimsshape::Tuple, row_first::Bool, xs...) = _hvncat(dimsshape, row_first, xs...) | ||
2266 | hvncat(dim::Int, xs...) = _hvncat(dim, true, xs...) | ||
2267 | |||
2268 | _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool) = _typed_hvncat(Any, dimsshape, row_first) | ||
2269 | _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs...) = _typed_hvncat(promote_eltypeof(xs...), dimsshape, row_first, xs...) | ||
2270 | _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::T...) where T<:Number = _typed_hvncat(T, dimsshape, row_first, xs...) | ||
2271 | _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::Number...) = _typed_hvncat(promote_typeof(xs...), dimsshape, row_first, xs...) | ||
2272 | _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::AbstractArray...) = _typed_hvncat(promote_eltype(xs...), dimsshape, row_first, xs...) | ||
2273 | _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::AbstractArray{T}...) where T = _typed_hvncat(T, dimsshape, row_first, xs...) | ||
2274 | |||
2275 | |||
2276 | typed_hvncat(T::Type, dimsshape::Tuple, row_first::Bool, xs...) = _typed_hvncat(T, dimsshape, row_first, xs...) | ||
2277 | typed_hvncat(T::Type, dim::Int, xs...) = _typed_hvncat(T, Val(dim), xs...) | ||
2278 | |||
2279 | # 1-dimensional hvncat methods | ||
2280 | |||
2281 | _typed_hvncat(::Type, ::Val{0}) = _typed_hvncat_0d_only_one() | ||
2282 | _typed_hvncat(T::Type, ::Val{0}, x) = fill(convert(T, x)) | ||
2283 | _typed_hvncat(T::Type, ::Val{0}, x::Number) = fill(convert(T, x)) | ||
2284 | _typed_hvncat(T::Type, ::Val{0}, x::AbstractArray) = convert.(T, x) | ||
2285 | _typed_hvncat(::Type, ::Val{0}, ::Any...) = _typed_hvncat_0d_only_one() | ||
2286 | _typed_hvncat(::Type, ::Val{0}, ::Number...) = _typed_hvncat_0d_only_one() | ||
2287 | _typed_hvncat(::Type, ::Val{0}, ::AbstractArray...) = _typed_hvncat_0d_only_one() | ||
2288 | |||
2289 | _typed_hvncat_0d_only_one() = | ||
2290 | throw(ArgumentError("a 0-dimensional array may only contain exactly one element")) | ||
2291 | |||
2292 | # `@constprop :aggressive` here to form constant `Val(dim)` type to get type stability | ||
2293 | @constprop :aggressive _typed_hvncat(T::Type, dim::Int, ::Bool, xs...) = _typed_hvncat(T, Val(dim), xs...) # catches from _hvncat type promoters | ||
2294 | |||
2295 | function _typed_hvncat(::Type{T}, ::Val{N}) where {T, N} | ||
2296 | N < 0 && | ||
2297 | throw(ArgumentError("concatenation dimension must be nonnegative")) | ||
2298 | return Array{T, N}(undef, ntuple(x -> 0, Val(N))) | ||
2299 | end | ||
2300 | |||
2301 | function _typed_hvncat(T::Type, ::Val{N}, xs::Number...) where N | ||
2302 | N < 0 && | ||
2303 | throw(ArgumentError("concatenation dimension must be nonnegative")) | ||
2304 | A = cat_similar(xs[1], T, (ntuple(x -> 1, Val(N - 1))..., length(xs))) | ||
2305 | hvncat_fill!(A, false, xs) | ||
2306 | return A | ||
2307 | end | ||
2308 | |||
2309 | function _typed_hvncat(::Type{T}, ::Val{N}, as::AbstractArray...) where {T, N} | ||
2310 | # optimization for arrays that can be concatenated by copying them linearly into the destination | ||
2311 | # conditions: the elements must all have 1-length dimensions above N | ||
2312 | length(as) > 0 || | ||
2313 | throw(ArgumentError("must have at least one element")) | ||
2314 | N < 0 && | ||
2315 | throw(ArgumentError("concatenation dimension must be nonnegative")) | ||
2316 | for a ∈ as | ||
2317 | ndims(a) <= N || all(x -> size(a, x) == 1, (N + 1):ndims(a)) || | ||
2318 | return _typed_hvncat(T, (ntuple(x -> 1, Val(N - 1))..., length(as), 1), false, as...) | ||
2319 | # the extra 1 is to avoid an infinite cycle | ||
2320 | end | ||
2321 | |||
2322 | nd = N | ||
2323 | |||
2324 | Ndim = 0 | ||
2325 | for i ∈ eachindex(as) | ||
2326 | Ndim += cat_size(as[i], N) | ||
2327 | nd = max(nd, cat_ndims(as[i])) | ||
2328 | for d ∈ 1:N - 1 | ||
2329 | cat_size(as[1], d) == cat_size(as[i], d) || throw(DimensionMismatch("mismatched size along axis $d in element $i")) | ||
2330 | end | ||
2331 | end | ||
2332 | |||
2333 | A = cat_similar(as[1], T, (ntuple(d -> size(as[1], d), N - 1)..., Ndim, ntuple(x -> 1, nd - N)...)) | ||
2334 | k = 1 | ||
2335 | for a ∈ as | ||
2336 | for i ∈ eachindex(a) | ||
2337 | A[k] = a[i] | ||
2338 | k += 1 | ||
2339 | end | ||
2340 | end | ||
2341 | return A | ||
2342 | end | ||
2343 | |||
2344 | function _typed_hvncat(::Type{T}, ::Val{N}, as...) where {T, N} | ||
2345 | length(as) > 0 || | ||
2346 | throw(ArgumentError("must have at least one element")) | ||
2347 | N < 0 && | ||
2348 | throw(ArgumentError("concatenation dimension must be nonnegative")) | ||
2349 | nd = N | ||
2350 | Ndim = 0 | ||
2351 | for i ∈ eachindex(as) | ||
2352 | Ndim += cat_size(as[i], N) | ||
2353 | nd = max(nd, cat_ndims(as[i])) | ||
2354 | for d ∈ 1:N-1 | ||
2355 | cat_size(as[i], d) == 1 || | ||
2356 | throw(DimensionMismatch("all dimensions of element $i other than $N must be of length 1")) | ||
2357 | end | ||
2358 | end | ||
2359 | |||
2360 | A = Array{T, nd}(undef, ntuple(x -> 1, Val(N - 1))..., Ndim, ntuple(x -> 1, nd - N)...) | ||
2361 | |||
2362 | k = 1 | ||
2363 | for a ∈ as | ||
2364 | if a isa AbstractArray | ||
2365 | lena = length(a) | ||
2366 | copyto!(A, k, a, 1, lena) | ||
2367 | k += lena | ||
2368 | else | ||
2369 | A[k] = a | ||
2370 | k += 1 | ||
2371 | end | ||
2372 | end | ||
2373 | return A | ||
2374 | end | ||
2375 | |||
2376 | # 0-dimensional cases for balanced and unbalanced hvncat method | ||
2377 | |||
2378 | _typed_hvncat(T::Type, ::Tuple{}, ::Bool, x...) = _typed_hvncat(T, Val(0), x...) | ||
2379 | _typed_hvncat(T::Type, ::Tuple{}, ::Bool, x::Number...) = _typed_hvncat(T, Val(0), x...) | ||
2380 | |||
2381 | |||
2382 | # balanced dimensions hvncat methods | ||
2383 | |||
2384 | _typed_hvncat(T::Type, dims::Tuple{Int}, ::Bool, as...) = _typed_hvncat_1d(T, dims[1], Val(false), as...) | ||
2385 | _typed_hvncat(T::Type, dims::Tuple{Int}, ::Bool, as::Number...) = _typed_hvncat_1d(T, dims[1], Val(false), as...) | ||
2386 | |||
2387 | function _typed_hvncat_1d(::Type{T}, ds::Int, ::Val{row_first}, as...) where {T, row_first} | ||
2388 | lengthas = length(as) | ||
2389 | ds > 0 || | ||
2390 | throw(ArgumentError("`dimsshape` argument must consist of positive integers")) | ||
2391 | lengthas == ds || | ||
2392 | throw(ArgumentError("number of elements does not match `dimshape` argument; expected $ds, got $lengthas")) | ||
2393 | if row_first | ||
2394 | return _typed_hvncat(T, Val(2), as...) | ||
2395 | else | ||
2396 | return _typed_hvncat(T, Val(1), as...) | ||
2397 | end | ||
2398 | end | ||
2399 | |||
2400 | function _typed_hvncat(::Type{T}, dims::NTuple{N, Int}, row_first::Bool, xs::Number...) where {T, N} | ||
2401 | all(>(0), dims) || | ||
2402 | throw(ArgumentError("`dims` argument must contain positive integers")) | ||
2403 | A = Array{T, N}(undef, dims...) | ||
2404 | lengtha = length(A) # Necessary to store result because throw blocks are being deoptimized right now, which leads to excessive allocations | ||
2405 | lengthx = length(xs) # Cuts from 3 allocations to 1. | ||
2406 | if lengtha != lengthx | ||
2407 | throw(ArgumentError("argument count does not match specified shape (expected $lengtha, got $lengthx)")) | ||
2408 | end | ||
2409 | hvncat_fill!(A, row_first, xs) | ||
2410 | return A | ||
2411 | end | ||
2412 | |||
2413 | function hvncat_fill!(A::Array, row_first::Bool, xs::Tuple) | ||
2414 | nr, nc = size(A, 1), size(A, 2) | ||
2415 | na = prod(size(A)[3:end]) | ||
2416 | len = length(xs) | ||
2417 | nrc = nr * nc | ||
2418 | if nrc * na != len | ||
2419 | throw(ArgumentError("argument count $(len) does not match specified shape $(size(A))")) | ||
2420 | end | ||
2421 | # putting these in separate functions leads to unnecessary allocations | ||
2422 | if row_first | ||
2423 | k = 1 | ||
2424 | for d ∈ 1:na | ||
2425 | dd = nrc * (d - 1) | ||
2426 | for i ∈ 1:nr | ||
2427 | Ai = dd + i | ||
2428 | for j ∈ 1:nc | ||
2429 | @inbounds A[Ai] = xs[k] | ||
2430 | k += 1 | ||
2431 | Ai += nr | ||
2432 | end | ||
2433 | end | ||
2434 | end | ||
2435 | else | ||
2436 | for k ∈ eachindex(xs) | ||
2437 | @inbounds A[k] = xs[k] | ||
2438 | end | ||
2439 | end | ||
2440 | end | ||
2441 | |||
2442 | function _typed_hvncat(T::Type, dims::NTuple{N, Int}, row_first::Bool, as...) where {N} | ||
2443 | # function barrier after calculating the max is necessary for high performance | ||
2444 | nd = max(maximum(cat_ndims(a) for a ∈ as), N) | ||
2445 | return _typed_hvncat_dims(T, (dims..., ntuple(x -> 1, nd - N)...), row_first, as) | ||
2446 | end | ||
2447 | |||
2448 | function _typed_hvncat_dims(::Type{T}, dims::NTuple{N, Int}, row_first::Bool, as::Tuple) where {T, N} | ||
2449 | length(as) > 0 || | ||
2450 | throw(ArgumentError("must have at least one element")) | ||
2451 | all(>(0), dims) || | ||
2452 | throw(ArgumentError("`dims` argument must contain positive integers")) | ||
2453 | |||
2454 | d1 = row_first ? 2 : 1 | ||
2455 | d2 = row_first ? 1 : 2 | ||
2456 | |||
2457 | outdims = zeros(Int, N) | ||
2458 | |||
2459 | # validate shapes for lowest level of concatenation | ||
2460 | d = findfirst(>(1), dims) | ||
2461 | if d !== nothing # all dims are 1 | ||
2462 | if row_first && d < 3 | ||
2463 | d = d == 1 ? 2 : 1 | ||
2464 | end | ||
2465 | nblocks = length(as) ÷ dims[d] | ||
2466 | for b ∈ 1:nblocks | ||
2467 | offset = ((b - 1) * dims[d]) | ||
2468 | startelementi = offset + 1 | ||
2469 | for i ∈ offset .+ (2:dims[d]) | ||
2470 | for dd ∈ 1:N | ||
2471 | dd == d && continue | ||
2472 | if cat_size(as[startelementi], dd) != cat_size(as[i], dd) | ||
2473 | throw(DimensionMismatch("incompatible shape in element $i")) | ||
2474 | end | ||
2475 | end | ||
2476 | end | ||
2477 | end | ||
2478 | end | ||
2479 | |||
2480 | # discover number of rows or columns | ||
2481 | for i ∈ 1:dims[d1] | ||
2482 | outdims[d1] += cat_size(as[i], d1) | ||
2483 | end | ||
2484 | |||
2485 | currentdims = zeros(Int, N) | ||
2486 | blockcount = 0 | ||
2487 | elementcount = 0 | ||
2488 | for i ∈ eachindex(as) | ||
2489 | elementcount += cat_length(as[i]) | ||
2490 | currentdims[d1] += cat_size(as[i], d1) | ||
2491 | if currentdims[d1] == outdims[d1] | ||
2492 | currentdims[d1] = 0 | ||
2493 | for d ∈ (d2, 3:N...) | ||
2494 | currentdims[d] += cat_size(as[i], d) | ||
2495 | if outdims[d] == 0 # unfixed dimension | ||
2496 | blockcount += 1 | ||
2497 | if blockcount == dims[d] | ||
2498 | outdims[d] = currentdims[d] | ||
2499 | currentdims[d] = 0 | ||
2500 | blockcount = 0 | ||
2501 | else | ||
2502 | break | ||
2503 | end | ||
2504 | else # fixed dimension | ||
2505 | if currentdims[d] == outdims[d] # end of dimension | ||
2506 | currentdims[d] = 0 | ||
2507 | elseif currentdims[d] < outdims[d] # dimension in progress | ||
2508 | break | ||
2509 | else # exceeded dimension | ||
2510 | throw(DimensionMismatch("argument $i has too many elements along axis $d")) | ||
2511 | end | ||
2512 | end | ||
2513 | end | ||
2514 | elseif currentdims[d1] > outdims[d1] # exceeded dimension | ||
2515 | throw(DimensionMismatch("argument $i has too many elements along axis $d1")) | ||
2516 | end | ||
2517 | end | ||
2518 | |||
2519 | outlen = prod(outdims) | ||
2520 | elementcount == outlen || | ||
2521 | throw(DimensionMismatch("mismatched number of elements; expected $(outlen), got $(elementcount)")) | ||
2522 | |||
2523 | # copy into final array | ||
2524 | A = cat_similar(as[1], T, outdims) | ||
2525 | # @assert all(==(0), currentdims) | ||
2526 | outdims .= 0 | ||
2527 | hvncat_fill!(A, currentdims, outdims, d1, d2, as) | ||
2528 | return A | ||
2529 | end | ||
2530 | |||
2531 | |||
2532 | # unbalanced dimensions hvncat methods | ||
2533 | |||
2534 | function _typed_hvncat(T::Type, shape::Tuple{Tuple}, row_first::Bool, xs...) | ||
2535 | length(shape[1]) > 0 || | ||
2536 | throw(ArgumentError("each level of `shape` argument must have at least one value")) | ||
2537 | return _typed_hvncat_1d(T, shape[1][1], Val(row_first), xs...) | ||
2538 | end | ||
2539 | |||
2540 | function _typed_hvncat(T::Type, shape::NTuple{N, Tuple}, row_first::Bool, as...) where {N} | ||
2541 | # function barrier after calculating the max is necessary for high performance | ||
2542 | nd = max(maximum(cat_ndims(a) for a ∈ as), N) | ||
2543 | return _typed_hvncat_shape(T, (shape..., ntuple(x -> shape[end], nd - N)...), row_first, as) | ||
2544 | end | ||
2545 | |||
2546 | function _typed_hvncat_shape(::Type{T}, shape::NTuple{N, Tuple}, row_first, as::Tuple) where {T, N} | ||
2547 | length(as) > 0 || | ||
2548 | throw(ArgumentError("must have at least one element")) | ||
2549 | all(>(0), tuple((shape...)...)) || | ||
2550 | throw(ArgumentError("`shape` argument must consist of positive integers")) | ||
2551 | |||
2552 | d1 = row_first ? 2 : 1 | ||
2553 | d2 = row_first ? 1 : 2 | ||
2554 | |||
2555 | shapev = collect(shape) # saves allocations later | ||
2556 | all(!isempty, shapev) || | ||
2557 | throw(ArgumentError("each level of `shape` argument must have at least one value")) | ||
2558 | length(shapev[end]) == 1 || | ||
2559 | throw(ArgumentError("last level of shape must contain only one integer")) | ||
2560 | shapelength = shapev[end][1] | ||
2561 | lengthas = length(as) | ||
2562 | shapelength == lengthas || throw(ArgumentError("number of elements does not match shape; expected $(shapelength), got $lengthas)")) | ||
2563 | # discover dimensions | ||
2564 | nd = max(N, cat_ndims(as[1])) | ||
2565 | outdims = fill(-1, nd) | ||
2566 | currentdims = zeros(Int, nd) | ||
2567 | blockcounts = zeros(Int, nd) | ||
2568 | shapepos = ones(Int, nd) | ||
2569 | |||
2570 | elementcount = 0 | ||
2571 | for i ∈ eachindex(as) | ||
2572 | elementcount += cat_length(as[i]) | ||
2573 | wasstartblock = false | ||
2574 | for d ∈ 1:N | ||
2575 | ad = (d < 3 && row_first) ? (d == 1 ? 2 : 1) : d | ||
2576 | dsize = cat_size(as[i], ad) | ||
2577 | blockcounts[d] += 1 | ||
2578 | |||
2579 | if d == 1 || i == 1 || wasstartblock | ||
2580 | currentdims[d] += dsize | ||
2581 | elseif dsize != cat_size(as[i - 1], ad) | ||
2582 | throw(DimensionMismatch("argument $i has a mismatched number of elements along axis $ad; \ | ||
2583 | expected $(cat_size(as[i - 1], ad)), got $dsize")) | ||
2584 | end | ||
2585 | |||
2586 | wasstartblock = blockcounts[d] == 1 # remember for next dimension | ||
2587 | |||
2588 | isendblock = blockcounts[d] == shapev[d][shapepos[d]] | ||
2589 | if isendblock | ||
2590 | if outdims[d] == -1 | ||
2591 | outdims[d] = currentdims[d] | ||
2592 | elseif outdims[d] != currentdims[d] | ||
2593 | throw(DimensionMismatch("argument $i has a mismatched number of elements along axis $ad; \ | ||
2594 | expected $(abs(outdims[d] - (currentdims[d] - dsize))), got $dsize")) | ||
2595 | end | ||
2596 | currentdims[d] = 0 | ||
2597 | blockcounts[d] = 0 | ||
2598 | shapepos[d] += 1 | ||
2599 | d > 1 && (blockcounts[d - 1] == 0 || | ||
2600 | throw(DimensionMismatch("shape in level $d is inconsistent; level counts must nest \ | ||
2601 | evenly into each other"))) | ||
2602 | end | ||
2603 | end | ||
2604 | end | ||
2605 | |||
2606 | outlen = prod(outdims) | ||
2607 | elementcount == outlen || | ||
2608 | throw(ArgumentError("mismatched number of elements; expected $(outlen), got $(elementcount)")) | ||
2609 | |||
2610 | if row_first | ||
2611 | outdims[1], outdims[2] = outdims[2], outdims[1] | ||
2612 | end | ||
2613 | |||
2614 | # @assert all(==(0), currentdims) | ||
2615 | # @assert all(==(0), blockcounts) | ||
2616 | |||
2617 | # copy into final array | ||
2618 | A = cat_similar(as[1], T, outdims) | ||
2619 | hvncat_fill!(A, currentdims, blockcounts, d1, d2, as) | ||
2620 | return A | ||
2621 | end | ||
2622 | |||
2623 | function hvncat_fill!(A::AbstractArray{T, N}, scratch1::Vector{Int}, scratch2::Vector{Int}, | ||
2624 | d1::Int, d2::Int, as::Tuple) where {T, N} | ||
2625 | N > 1 || throw(ArgumentError("dimensions of the destination array must be at least 2")) | ||
2626 | length(scratch1) == length(scratch2) == N || | ||
2627 | throw(ArgumentError("scratch vectors must have as many elements as the destination array has dimensions")) | ||
2628 | 0 < d1 < 3 && | ||
2629 | 0 < d2 < 3 && | ||
2630 | d1 != d2 || | ||
2631 | throw(ArgumentError("d1 and d2 must be either 1 or 2, exclusive.")) | ||
2632 | outdims = size(A) | ||
2633 | offsets = scratch1 | ||
2634 | inneroffsets = scratch2 | ||
2635 | for a ∈ as | ||
2636 | if isa(a, AbstractArray) | ||
2637 | for ai ∈ a | ||
2638 | @inbounds Ai = hvncat_calcindex(offsets, inneroffsets, outdims, N) | ||
2639 | A[Ai] = ai | ||
2640 | |||
2641 | @inbounds for j ∈ 1:N | ||
2642 | inneroffsets[j] += 1 | ||
2643 | inneroffsets[j] < cat_size(a, j) && break | ||
2644 | inneroffsets[j] = 0 | ||
2645 | end | ||
2646 | end | ||
2647 | else | ||
2648 | @inbounds Ai = hvncat_calcindex(offsets, inneroffsets, outdims, N) | ||
2649 | A[Ai] = a | ||
2650 | end | ||
2651 | |||
2652 | @inbounds for j ∈ (d1, d2, 3:N...) | ||
2653 | offsets[j] += cat_size(a, j) | ||
2654 | offsets[j] < outdims[j] && break | ||
2655 | offsets[j] = 0 | ||
2656 | end | ||
2657 | end | ||
2658 | end | ||
2659 | |||
2660 | @propagate_inbounds function hvncat_calcindex(offsets::Vector{Int}, inneroffsets::Vector{Int}, | ||
2661 | outdims::Tuple{Vararg{Int}}, nd::Int) | ||
2662 | Ai = inneroffsets[1] + offsets[1] + 1 | ||
2663 | for j ∈ 2:nd | ||
2664 | increment = inneroffsets[j] + offsets[j] | ||
2665 | for k ∈ 1:j-1 | ||
2666 | increment *= outdims[k] | ||
2667 | end | ||
2668 | Ai += increment | ||
2669 | end | ||
2670 | Ai | ||
2671 | end | ||
2672 | |||
2673 | """ | ||
2674 | stack(iter; [dims]) | ||
2675 | |||
2676 | Combine a collection of arrays (or other iterable objects) of equal size | ||
2677 | into one larger array, by arranging them along one or more new dimensions. | ||
2678 | |||
2679 | By default the axes of the elements are placed first, | ||
2680 | giving `size(result) = (size(first(iter))..., size(iter)...)`. | ||
2681 | This has the same order of elements as [`Iterators.flatten`](@ref)`(iter)`. | ||
2682 | |||
2683 | With keyword `dims::Integer`, instead the `i`th element of `iter` becomes the slice | ||
2684 | [`selectdim`](@ref)`(result, dims, i)`, so that `size(result, dims) == length(iter)`. | ||
2685 | In this case `stack` reverses the action of [`eachslice`](@ref) with the same `dims`. | ||
2686 | |||
2687 | The various [`cat`](@ref) functions also combine arrays. However, these all | ||
2688 | extend the arrays' existing (possibly trivial) dimensions, rather than placing | ||
2689 | the arrays along new dimensions. | ||
2690 | They also accept arrays as separate arguments, rather than a single collection. | ||
2691 | |||
2692 | !!! compat "Julia 1.9" | ||
2693 | This function requires at least Julia 1.9. | ||
2694 | |||
2695 | # Examples | ||
2696 | ```jldoctest | ||
2697 | julia> vecs = (1:2, [30, 40], Float32[500, 600]); | ||
2698 | |||
2699 | julia> mat = stack(vecs) | ||
2700 | 2×3 Matrix{Float32}: | ||
2701 | 1.0 30.0 500.0 | ||
2702 | 2.0 40.0 600.0 | ||
2703 | |||
2704 | julia> mat == hcat(vecs...) == reduce(hcat, collect(vecs)) | ||
2705 | true | ||
2706 | |||
2707 | julia> vec(mat) == vcat(vecs...) == reduce(vcat, collect(vecs)) | ||
2708 | true | ||
2709 | |||
2710 | julia> stack(zip(1:4, 10:99)) # accepts any iterators of iterators | ||
2711 | 2×4 Matrix{Int64}: | ||
2712 | 1 2 3 4 | ||
2713 | 10 11 12 13 | ||
2714 | |||
2715 | julia> vec(ans) == collect(Iterators.flatten(zip(1:4, 10:99))) | ||
2716 | true | ||
2717 | |||
2718 | julia> stack(vecs; dims=1) # unlike any cat function, 1st axis of vecs[1] is 2nd axis of result | ||
2719 | 3×2 Matrix{Float32}: | ||
2720 | 1.0 2.0 | ||
2721 | 30.0 40.0 | ||
2722 | 500.0 600.0 | ||
2723 | |||
2724 | julia> x = rand(3,4); | ||
2725 | |||
2726 | julia> x == stack(eachcol(x)) == stack(eachrow(x), dims=1) # inverse of eachslice | ||
2727 | true | ||
2728 | ``` | ||
2729 | |||
2730 | Higher-dimensional examples: | ||
2731 | |||
2732 | ```jldoctest | ||
2733 | julia> A = rand(5, 7, 11); | ||
2734 | |||
2735 | julia> E = eachslice(A, dims=2); # a vector of matrices | ||
2736 | |||
2737 | julia> (element = size(first(E)), container = size(E)) | ||
2738 | (element = (5, 11), container = (7,)) | ||
2739 | |||
2740 | julia> stack(E) |> size | ||
2741 | (5, 11, 7) | ||
2742 | |||
2743 | julia> stack(E) == stack(E; dims=3) == cat(E...; dims=3) | ||
2744 | true | ||
2745 | |||
2746 | julia> A == stack(E; dims=2) | ||
2747 | true | ||
2748 | |||
2749 | julia> M = (fill(10i+j, 2, 3) for i in 1:5, j in 1:7); | ||
2750 | |||
2751 | julia> (element = size(first(M)), container = size(M)) | ||
2752 | (element = (2, 3), container = (5, 7)) | ||
2753 | |||
2754 | julia> stack(M) |> size # keeps all dimensions | ||
2755 | (2, 3, 5, 7) | ||
2756 | |||
2757 | julia> stack(M; dims=1) |> size # vec(container) along dims=1 | ||
2758 | (35, 2, 3) | ||
2759 | |||
2760 | julia> hvcat(5, M...) |> size # hvcat puts matrices next to each other | ||
2761 | (14, 15) | ||
2762 | ``` | ||
2763 | """ | ||
2764 | stack(iter; dims=:) = _stack(dims, iter) | ||
2765 | |||
2766 | """ | ||
2767 | stack(f, args...; [dims]) | ||
2768 | |||
2769 | Apply a function to each element of a collection, and `stack` the result. | ||
2770 | Or to several collections, [`zip`](@ref)ped together. | ||
2771 | |||
2772 | The function should return arrays (or tuples, or other iterators) all of the same size. | ||
2773 | These become slices of the result, each separated along `dims` (if given) or by default | ||
2774 | along the last dimensions. | ||
2775 | |||
2776 | See also [`mapslices`](@ref), [`eachcol`](@ref). | ||
2777 | |||
2778 | # Examples | ||
2779 | ```jldoctest | ||
2780 | julia> stack(c -> (c, c-32), "julia") | ||
2781 | 2×5 Matrix{Char}: | ||
2782 | 'j' 'u' 'l' 'i' 'a' | ||
2783 | 'J' 'U' 'L' 'I' 'A' | ||
2784 | |||
2785 | julia> stack(eachrow([1 2 3; 4 5 6]), (10, 100); dims=1) do row, n | ||
2786 | vcat(row, row .* n, row ./ n) | ||
2787 | end | ||
2788 | 2×9 Matrix{Float64}: | ||
2789 | 1.0 2.0 3.0 10.0 20.0 30.0 0.1 0.2 0.3 | ||
2790 | 4.0 5.0 6.0 400.0 500.0 600.0 0.04 0.05 0.06 | ||
2791 | ``` | ||
2792 | """ | ||
2793 | stack(f, iter; dims=:) = _stack(dims, f(x) for x in iter) | ||
2794 | stack(f, xs, yzs...; dims=:) = _stack(dims, f(xy...) for xy in zip(xs, yzs...)) | ||
2795 | |||
2796 | _stack(dims::Union{Integer, Colon}, iter) = _stack(dims, IteratorSize(iter), iter) | ||
2797 | |||
2798 | _stack(dims, ::IteratorSize, iter) = _stack(dims, collect(iter)) | ||
2799 | |||
2800 | function _stack(dims, ::Union{HasShape, HasLength}, iter) | ||
2801 | S = @default_eltype iter | ||
2802 | T = S != Union{} ? eltype(S) : Any # Union{} occurs for e.g. stack(1,2), postpone the error | ||
2803 | if isconcretetype(T) | ||
2804 | _typed_stack(dims, T, S, iter) | ||
2805 | else # Need to look inside, but shouldn't run an expensive iterator twice: | ||
2806 | array = iter isa Union{Tuple, AbstractArray} ? iter : collect(iter) | ||
2807 | isempty(array) && return _empty_stack(dims, T, S, iter) | ||
2808 | T2 = mapreduce(eltype, promote_type, array) | ||
2809 | _typed_stack(dims, T2, eltype(array), array) | ||
2810 | end | ||
2811 | end | ||
2812 | |||
2813 | function _typed_stack(::Colon, ::Type{T}, ::Type{S}, A, Aax=_iterator_axes(A)) where {T, S} | ||
2814 | xit = iterate(A) | ||
2815 | nothing === xit && return _empty_stack(:, T, S, A) | ||
2816 | x1, _ = xit | ||
2817 | ax1 = _iterator_axes(x1) | ||
2818 | B = similar(_ensure_array(x1), T, ax1..., Aax...) | ||
2819 | off = firstindex(B) | ||
2820 | len = length(x1) | ||
2821 | while xit !== nothing | ||
2822 | x, state = xit | ||
2823 | _stack_size_check(x, ax1) | ||
2824 | copyto!(B, off, x) | ||
2825 | off += len | ||
2826 | xit = iterate(A, state) | ||
2827 | end | ||
2828 | B | ||
2829 | end | ||
2830 | |||
2831 | _iterator_axes(x) = _iterator_axes(x, IteratorSize(x)) | ||
2832 | _iterator_axes(x, ::HasLength) = (OneTo(length(x)),) | ||
2833 | _iterator_axes(x, ::IteratorSize) = axes(x) | ||
2834 | |||
2835 | # For some dims values, stack(A; dims) == stack(vec(A)), and the : path will be faster | ||
2836 | _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, A) where {T,S} = | ||
2837 | _typed_stack(dims, T, S, IteratorSize(S), A) | ||
2838 | _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::HasLength, A) where {T,S} = | ||
2839 | _typed_stack(dims, T, S, HasShape{1}(), A) | ||
2840 | function _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::HasShape{N}, A) where {T,S,N} | ||
2841 | if dims == N+1 | ||
2842 | _typed_stack(:, T, S, A, (_vec_axis(A),)) | ||
2843 | else | ||
2844 | _dim_stack(dims, T, S, A) | ||
2845 | end | ||
2846 | end | ||
2847 | _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::IteratorSize, A) where {T,S} = | ||
2848 | _dim_stack(dims, T, S, A) | ||
2849 | |||
2850 | _vec_axis(A, ax=_iterator_axes(A)) = length(ax) == 1 ? only(ax) : OneTo(prod(length, ax; init=1)) | ||
2851 | |||
2852 | @constprop :aggressive function _dim_stack(dims::Integer, ::Type{T}, ::Type{S}, A) where {T,S} | ||
2853 | xit = Iterators.peel(A) | ||
2854 | nothing === xit && return _empty_stack(dims, T, S, A) | ||
2855 | x1, xrest = xit | ||
2856 | ax1 = _iterator_axes(x1) | ||
2857 | N1 = length(ax1)+1 | ||
2858 | dims in 1:N1 || throw(ArgumentError(LazyString("cannot stack slices ndims(x) = ", N1-1, " along dims = ", dims))) | ||
2859 | |||
2860 | newaxis = _vec_axis(A) | ||
2861 | outax = ntuple(d -> d==dims ? newaxis : ax1[d - (d>dims)], N1) | ||
2862 | B = similar(_ensure_array(x1), T, outax...) | ||
2863 | |||
2864 | if dims == 1 | ||
2865 | _dim_stack!(Val(1), B, x1, xrest) | ||
2866 | elseif dims == 2 | ||
2867 | _dim_stack!(Val(2), B, x1, xrest) | ||
2868 | else | ||
2869 | _dim_stack!(Val(dims), B, x1, xrest) | ||
2870 | end | ||
2871 | B | ||
2872 | end | ||
2873 | |||
2874 | function _dim_stack!(::Val{dims}, B::AbstractArray, x1, xrest) where {dims} | ||
2875 | before = ntuple(d -> Colon(), dims - 1) | ||
2876 | after = ntuple(d -> Colon(), ndims(B) - dims) | ||
2877 | |||
2878 | i = firstindex(B, dims) | ||
2879 | copyto!(view(B, before..., i, after...), x1) | ||
2880 | |||
2881 | for x in xrest | ||
2882 | _stack_size_check(x, _iterator_axes(x1)) | ||
2883 | i += 1 | ||
2884 | @inbounds copyto!(view(B, before..., i, after...), x) | ||
2885 | end | ||
2886 | end | ||
2887 | |||
2888 | @inline function _stack_size_check(x, ax1::Tuple) | ||
2889 | if _iterator_axes(x) != ax1 | ||
2890 | uax1 = map(UnitRange, ax1) | ||
2891 | uaxN = map(UnitRange, axes(x)) | ||
2892 | throw(DimensionMismatch( | ||
2893 | LazyString("stack expects uniform slices, got axes(x) == ", uaxN, " while first had ", uax1))) | ||
2894 | end | ||
2895 | end | ||
2896 | |||
2897 | _ensure_array(x::AbstractArray) = x | ||
2898 | _ensure_array(x) = 1:0 # passed to similar, makes stack's output an Array | ||
2899 | |||
2900 | _empty_stack(_...) = throw(ArgumentError("`stack` on an empty collection is not allowed")) | ||
2901 | |||
2902 | |||
2903 | ## Reductions and accumulates ## | ||
2904 | |||
2905 | function isequal(A::AbstractArray, B::AbstractArray) | ||
2906 | if A === B return true end | ||
2907 | if axes(A) != axes(B) | ||
2908 | return false | ||
2909 | end | ||
2910 | for (a, b) in zip(A, B) | ||
2911 | if !isequal(a, b) | ||
2912 | return false | ||
2913 | end | ||
2914 | end | ||
2915 | return true | ||
2916 | end | ||
2917 | |||
2918 | function cmp(A::AbstractVector, B::AbstractVector) | ||
2919 | for (a, b) in zip(A, B) | ||
2920 | if !isequal(a, b) | ||
2921 | return isless(a, b) ? -1 : 1 | ||
2922 | end | ||
2923 | end | ||
2924 | return cmp(length(A), length(B)) | ||
2925 | end | ||
2926 | |||
2927 | """ | ||
2928 | isless(A::AbstractVector, B::AbstractVector) | ||
2929 | |||
2930 | Return `true` when `A` is less than `B` in lexicographic order. | ||
2931 | """ | ||
2932 | isless(A::AbstractVector, B::AbstractVector) = cmp(A, B) < 0 | ||
2933 | |||
2934 | function (==)(A::AbstractArray, B::AbstractArray) | ||
2935 | if axes(A) != axes(B) | ||
2936 | return false | ||
2937 | end | ||
2938 | anymissing = false | ||
2939 | for (a, b) in zip(A, B) | ||
2940 | eq = (a == b) | ||
2941 | if ismissing(eq) | ||
2942 | anymissing = true | ||
2943 | elseif !eq | ||
2944 | return false | ||
2945 | end | ||
2946 | end | ||
2947 | return anymissing ? missing : true | ||
2948 | end | ||
2949 | |||
2950 | # _sub2ind and _ind2sub | ||
2951 | # fallbacks | ||
2952 | function _sub2ind(A::AbstractArray, I...) | ||
2953 | @inline | ||
2954 | _sub2ind(axes(A), I...) | ||
2955 | end | ||
2956 | |||
2957 | function _ind2sub(A::AbstractArray, ind) | ||
2958 | @inline | ||
2959 | _ind2sub(axes(A), ind) | ||
2960 | end | ||
2961 | |||
2962 | # 0-dimensional arrays and indexing with [] | ||
2963 | _sub2ind(::Tuple{}) = 1 | ||
2964 | _sub2ind(::DimsInteger) = 1 | ||
2965 | _sub2ind(::Indices) = 1 | ||
2966 | _sub2ind(::Tuple{}, I::Integer...) = (@inline; _sub2ind_recurse((), 1, 1, I...)) | ||
2967 | |||
2968 | # Generic cases | ||
2969 | _sub2ind(dims::DimsInteger, I::Integer...) = (@inline; _sub2ind_recurse(dims, 1, 1, I...)) | ||
2970 | _sub2ind(inds::Indices, I::Integer...) = (@inline; _sub2ind_recurse(inds, 1, 1, I...)) | ||
2971 | # In 1d, there's a question of whether we're doing cartesian indexing | ||
2972 | # or linear indexing. Support only the former. | ||
2973 | _sub2ind(inds::Indices{1}, I::Integer...) = | ||
2974 | throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) | ||
2975 | _sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@inline; _sub2ind_recurse(inds, 1, 1, I...)) # only OneTo is safe | ||
2976 | _sub2ind(inds::Tuple{OneTo}, i::Integer) = i | ||
2977 | |||
2978 | _sub2ind_recurse(::Any, L, ind) = ind | ||
2979 | function _sub2ind_recurse(::Tuple{}, L, ind, i::Integer, I::Integer...) | ||
2980 | @inline | ||
2981 | _sub2ind_recurse((), L, ind+(i-1)*L, I...) | ||
2982 | end | ||
2983 | function _sub2ind_recurse(inds, L, ind, i::Integer, I::Integer...) | ||
2984 | @inline | ||
2985 | r1 = inds[1] | ||
2986 | _sub2ind_recurse(tail(inds), nextL(L, r1), ind+offsetin(i, r1)*L, I...) | ||
2987 | end | ||
2988 | |||
2989 | nextL(L, l::Integer) = L*l | ||
2990 | nextL(L, r::AbstractUnitRange) = L*length(r) | ||
2991 | nextL(L, r::Slice) = L*length(r.indices) | ||
2992 | offsetin(i, l::Integer) = i-1 | ||
2993 | offsetin(i, r::AbstractUnitRange) = i-first(r) | ||
2994 | |||
2995 | _ind2sub(::Tuple{}, ind::Integer) = (@inline; ind == 1 ? () : throw(BoundsError())) | ||
2996 | _ind2sub(dims::DimsInteger, ind::Integer) = (@inline; _ind2sub_recurse(dims, ind-1)) | ||
2997 | _ind2sub(inds::Indices, ind::Integer) = (@inline; _ind2sub_recurse(inds, ind-1)) | ||
2998 | _ind2sub(inds::Indices{1}, ind::Integer) = | ||
2999 | throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) | ||
3000 | _ind2sub(inds::Tuple{OneTo}, ind::Integer) = (ind,) | ||
3001 | |||
3002 | _ind2sub_recurse(::Tuple{}, ind) = (ind+1,) | ||
3003 | function _ind2sub_recurse(indslast::NTuple{1}, ind) | ||
3004 | @inline | ||
3005 | (_lookup(ind, indslast[1]),) | ||
3006 | end | ||
3007 | function _ind2sub_recurse(inds, ind) | ||
3008 | @inline | ||
3009 | r1 = inds[1] | ||
3010 | indnext, f, l = _div(ind, r1) | ||
3011 | (ind-l*indnext+f, _ind2sub_recurse(tail(inds), indnext)...) | ||
3012 | end | ||
3013 | |||
3014 | _lookup(ind, d::Integer) = ind+1 | ||
3015 | _lookup(ind, r::AbstractUnitRange) = ind+first(r) | ||
3016 | _div(ind, d::Integer) = div(ind, d), 1, d | ||
3017 | _div(ind, r::AbstractUnitRange) = (d = length(r); (div(ind, d), first(r), d)) | ||
3018 | |||
3019 | # Vectorized forms | ||
3020 | function _sub2ind(inds::Indices{1}, I1::AbstractVector{T}, I::AbstractVector{T}...) where T<:Integer | ||
3021 | throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) | ||
3022 | end | ||
3023 | _sub2ind(inds::Tuple{OneTo}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = | ||
3024 | _sub2ind_vecs(inds, I1, I...) | ||
3025 | _sub2ind(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = | ||
3026 | _sub2ind_vecs(inds, I1, I...) | ||
3027 | function _sub2ind_vecs(inds, I::AbstractVector...) | ||
3028 | I1 = I[1] | ||
3029 | Iinds = axes1(I1) | ||
3030 | for j = 2:length(I) | ||
3031 | axes1(I[j]) == Iinds || throw(DimensionMismatch("indices of I[1] ($(Iinds)) does not match indices of I[$j] ($(axes1(I[j])))")) | ||
3032 | end | ||
3033 | Iout = similar(I1) | ||
3034 | _sub2ind!(Iout, inds, Iinds, I) | ||
3035 | Iout | ||
3036 | end | ||
3037 | |||
3038 | function _sub2ind!(Iout, inds, Iinds, I) | ||
3039 | @noinline | ||
3040 | for i in Iinds | ||
3041 | # Iout[i] = _sub2ind(inds, map(Ij -> Ij[i], I)...) | ||
3042 | Iout[i] = sub2ind_vec(inds, i, I) | ||
3043 | end | ||
3044 | Iout | ||
3045 | end | ||
3046 | |||
3047 | sub2ind_vec(inds, i, I) = (@inline; _sub2ind(inds, _sub2ind_vec(i, I...)...)) | ||
3048 | _sub2ind_vec(i, I1, I...) = (@inline; (I1[i], _sub2ind_vec(i, I...)...)) | ||
3049 | _sub2ind_vec(i) = () | ||
3050 | |||
3051 | function _ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) where N | ||
3052 | M = length(ind) | ||
3053 | t = ntuple(n->similar(ind),Val(N)) | ||
3054 | for (i,idx) in pairs(IndexLinear(), ind) | ||
3055 | sub = _ind2sub(inds, idx) | ||
3056 | for j = 1:N | ||
3057 | t[j][i] = sub[j] | ||
3058 | end | ||
3059 | end | ||
3060 | t | ||
3061 | end | ||
3062 | |||
3063 | ## iteration utilities ## | ||
3064 | |||
3065 | """ | ||
3066 | foreach(f, c...) -> Nothing | ||
3067 | |||
3068 | Call function `f` on each element of iterable `c`. | ||
3069 | For multiple iterable arguments, `f` is called elementwise, and iteration stops when | ||
3070 | any iterator is finished. | ||
3071 | |||
3072 | `foreach` should be used instead of [`map`](@ref) when the results of `f` are not | ||
3073 | needed, for example in `foreach(println, array)`. | ||
3074 | |||
3075 | # Examples | ||
3076 | ```jldoctest | ||
3077 | julia> tri = 1:3:7; res = Int[]; | ||
3078 | |||
3079 | julia> foreach(x -> push!(res, x^2), tri) | ||
3080 | |||
3081 | julia> res | ||
3082 | 3-element Vector{$(Int)}: | ||
3083 | 1 | ||
3084 | 16 | ||
3085 | 49 | ||
3086 | |||
3087 | julia> foreach((x, y) -> println(x, " with ", y), tri, 'a':'z') | ||
3088 | 1 with a | ||
3089 | 4 with b | ||
3090 | 7 with c | ||
3091 | ``` | ||
3092 | """ | ||
3093 | foreach(f) = (f(); nothing) | ||
3094 | foreach(f, itr) = (for x in itr; f(x); end; nothing) | ||
3095 | foreach(f, itrs...) = (for z in zip(itrs...); f(z...); end; nothing) | ||
3096 | |||
3097 | ## map over arrays ## | ||
3098 | |||
3099 | ## transform any set of dimensions | ||
3100 | ## dims specifies which dimensions will be transformed. for example | ||
3101 | ## dims==1:2 will call f on all slices A[:,:,...] | ||
3102 | """ | ||
3103 | mapslices(f, A; dims) | ||
3104 | |||
3105 | Transform the given dimensions of array `A` by applying a function `f` on each slice | ||
3106 | of the form `A[..., :, ..., :, ...]`, with a colon at each `d` in `dims`. The results are | ||
3107 | concatenated along the remaining dimensions. | ||
3108 | |||
3109 | For example, if `dims = [1,2]` and `A` is 4-dimensional, then `f` is called on `x = A[:,:,i,j]` | ||
3110 | for all `i` and `j`, and `f(x)` becomes `R[:,:,i,j]` in the result `R`. | ||
3111 | |||
3112 | See also [`eachcol`](@ref) or [`eachslice`](@ref), used with [`map`](@ref) or [`stack`](@ref). | ||
3113 | |||
3114 | # Examples | ||
3115 | ```jldoctest | ||
3116 | julia> A = reshape(1:30,(2,5,3)) | ||
3117 | 2×5×3 reshape(::UnitRange{$Int}, 2, 5, 3) with eltype $Int: | ||
3118 | [:, :, 1] = | ||
3119 | 1 3 5 7 9 | ||
3120 | 2 4 6 8 10 | ||
3121 | |||
3122 | [:, :, 2] = | ||
3123 | 11 13 15 17 19 | ||
3124 | 12 14 16 18 20 | ||
3125 | |||
3126 | [:, :, 3] = | ||
3127 | 21 23 25 27 29 | ||
3128 | 22 24 26 28 30 | ||
3129 | |||
3130 | julia> f(x::Matrix) = fill(x[1,1], 1,4); # returns a 1×4 matrix | ||
3131 | |||
3132 | julia> B = mapslices(f, A, dims=(1,2)) | ||
3133 | 1×4×3 Array{$Int, 3}: | ||
3134 | [:, :, 1] = | ||
3135 | 1 1 1 1 | ||
3136 | |||
3137 | [:, :, 2] = | ||
3138 | 11 11 11 11 | ||
3139 | |||
3140 | [:, :, 3] = | ||
3141 | 21 21 21 21 | ||
3142 | |||
3143 | julia> f2(x::AbstractMatrix) = fill(x[1,1], 1,4); | ||
3144 | |||
3145 | julia> B == stack(f2, eachslice(A, dims=3)) | ||
3146 | true | ||
3147 | |||
3148 | julia> g(x) = x[begin] // x[end-1]; # returns a number | ||
3149 | |||
3150 | julia> mapslices(g, A, dims=[1,3]) | ||
3151 | 1×5×1 Array{Rational{$Int}, 3}: | ||
3152 | [:, :, 1] = | ||
3153 | 1//21 3//23 1//5 7//27 9//29 | ||
3154 | |||
3155 | julia> map(g, eachslice(A, dims=2)) | ||
3156 | 5-element Vector{Rational{$Int}}: | ||
3157 | 1//21 | ||
3158 | 3//23 | ||
3159 | 1//5 | ||
3160 | 7//27 | ||
3161 | 9//29 | ||
3162 | |||
3163 | julia> mapslices(sum, A; dims=(1,3)) == sum(A; dims=(1,3)) | ||
3164 | true | ||
3165 | ``` | ||
3166 | |||
3167 | Notice that in `eachslice(A; dims=2)`, the specified dimension is the | ||
3168 | one *without* a colon in the slice. This is `view(A,:,i,:)`, whereas | ||
3169 | `mapslices(f, A; dims=(1,3))` uses `A[:,i,:]`. The function `f` may mutate | ||
3170 | values in the slice without affecting `A`. | ||
3171 | """ | ||
3172 | function mapslices(f, A::AbstractArray; dims) | ||
3173 | isempty(dims) && return map(f, A) | ||
3174 | |||
3175 | for d in dims | ||
3176 | d isa Integer || throw(ArgumentError("mapslices: dimension must be an integer, got $d")) | ||
3177 | d >= 1 || throw(ArgumentError("mapslices: dimension must be ≥ 1, got $d")) | ||
3178 | # Indexing a matrix M[:,1,:] produces a 1-column matrix, but dims=(1,3) here | ||
3179 | # would otherwise ignore 3, and slice M[:,i]. Previously this gave error: | ||
3180 | # BoundsError: attempt to access 2-element Vector{Any} at index [3] | ||
3181 | d > ndims(A) && throw(ArgumentError("mapslices does not accept dimensions > ndims(A) = $(ndims(A)), got $d")) | ||
3182 | end | ||
3183 | dim_mask = ntuple(d -> d in dims, ndims(A)) | ||
3184 | |||
3185 | # Apply the function to the first slice in order to determine the next steps | ||
3186 | idx1 = ntuple(d -> d in dims ? (:) : firstindex(A,d), ndims(A)) | ||
3187 | Aslice = A[idx1...] | ||
3188 | r1 = f(Aslice) | ||
3189 | |||
3190 | res1 = if r1 isa AbstractArray && ndims(r1) > 0 | ||
3191 | n = sum(dim_mask) | ||
3192 | if ndims(r1) > n && any(ntuple(d -> size(r1,d+n)>1, ndims(r1)-n)) | ||
3193 | s = size(r1)[1:n] | ||
3194 | throw(DimensionMismatch("mapslices cannot assign slice f(x) of size $(size(r1)) into output of size $s")) | ||
3195 | end | ||
3196 | r1 | ||
3197 | else | ||
3198 | # If the result of f on a single slice is a scalar then we add singleton | ||
3199 | # dimensions. When adding the dimensions, we have to respect the | ||
3200 | # index type of the input array (e.g. in the case of OffsetArrays) | ||
3201 | _res1 = similar(Aslice, typeof(r1), reduced_indices(Aslice, 1:ndims(Aslice))) | ||
3202 | _res1[begin] = r1 | ||
3203 | _res1 | ||
3204 | end | ||
3205 | |||
3206 | # Determine result size and allocate. We always pad ndims(res1) out to length(dims): | ||
3207 | din = Ref(0) | ||
3208 | Rsize = ntuple(ndims(A)) do d | ||
3209 | if d in dims | ||
3210 | axes(res1, din[] += 1) | ||
3211 | else | ||
3212 | axes(A,d) | ||
3213 | end | ||
3214 | end | ||
3215 | R = similar(res1, Rsize) | ||
3216 | |||
3217 | # Determine iteration space. It will be convenient in the loop to mask N-dimensional | ||
3218 | # CartesianIndices, with some trivial dimensions: | ||
3219 | itershape = ntuple(d -> d in dims ? Base.OneTo(1) : axes(A,d), ndims(A)) | ||
3220 | indices = Iterators.drop(CartesianIndices(itershape), 1) | ||
3221 | |||
3222 | # That skips the first element, which we already have: | ||
3223 | ridx = ntuple(d -> d in dims ? Slice(axes(R,d)) : firstindex(A,d), ndims(A)) | ||
3224 | concatenate_setindex!(R, res1, ridx...) | ||
3225 | |||
3226 | # In some cases, we can re-use the first slice for a dramatic performance | ||
3227 | # increase. The slice itself must be mutable and the result cannot contain | ||
3228 | # any mutable containers. The following errs on the side of being overly | ||
3229 | # strict (#18570 & #21123). | ||
3230 | safe_for_reuse = isa(Aslice, StridedArray) && | ||
3231 | (isa(r1, Number) || (isa(r1, AbstractArray) && eltype(r1) <: Number)) | ||
3232 | |||
3233 | _inner_mapslices!(R, indices, f, A, dim_mask, Aslice, safe_for_reuse) | ||
3234 | return R | ||
3235 | end | ||
3236 | |||
3237 | @noinline function _inner_mapslices!(R, indices, f, A, dim_mask, Aslice, safe_for_reuse) | ||
3238 | must_extend = any(dim_mask .& size(R) .> 1) | ||
3239 | if safe_for_reuse | ||
3240 | # when f returns an array, R[ridx...] = f(Aslice) line copies elements, | ||
3241 | # so we can reuse Aslice | ||
3242 | for I in indices | ||
3243 | idx = ifelse.(dim_mask, Slice.(axes(A)), Tuple(I)) | ||
3244 | _unsafe_getindex!(Aslice, A, idx...) | ||
3245 | r = f(Aslice) | ||
3246 | if r isa AbstractArray || must_extend | ||
3247 | ridx = ifelse.(dim_mask, Slice.(axes(R)), Tuple(I)) | ||
3248 | R[ridx...] = r | ||
3249 | else | ||
3250 | ridx = ifelse.(dim_mask, first.(axes(R)), Tuple(I)) | ||
3251 | R[ridx...] = r | ||
3252 | end | ||
3253 | end | ||
3254 | else | ||
3255 | # we can't guarantee safety (#18524), so allocate new storage for each slice | ||
3256 | for I in indices | ||
3257 | idx = ifelse.(dim_mask, Slice.(axes(A)), Tuple(I)) | ||
3258 | ridx = ifelse.(dim_mask, Slice.(axes(R)), Tuple(I)) | ||
3259 | concatenate_setindex!(R, f(A[idx...]), ridx...) | ||
3260 | end | ||
3261 | end | ||
3262 | end | ||
3263 | |||
3264 | concatenate_setindex!(R, v, I...) = (R[I...] .= (v,); R) | ||
3265 | concatenate_setindex!(R, X::AbstractArray, I...) = (R[I...] = X) | ||
3266 | |||
3267 | ## 0 arguments | ||
3268 | |||
3269 | map(f) = f() | ||
3270 | |||
3271 | ## 1 argument | ||
3272 | |||
3273 | function map!(f::F, dest::AbstractArray, A::AbstractArray) where F | ||
3274 | for (i,j) in zip(eachindex(dest),eachindex(A)) | ||
3275 | val = f(@inbounds A[j]) | ||
3276 | @inbounds dest[i] = val | ||
3277 | end | ||
3278 | return dest | ||
3279 | end | ||
3280 | |||
3281 | # map on collections | ||
3282 | map(f, A::AbstractArray) = collect_similar(A, Generator(f,A)) | ||
3283 | |||
3284 | mapany(f, A::AbstractArray) = map!(f, Vector{Any}(undef, length(A)), A) | ||
3285 | mapany(f, itr) = Any[f(x) for x in itr] | ||
3286 | |||
3287 | """ | ||
3288 | map(f, c...) -> collection | ||
3289 | |||
3290 | Transform collection `c` by applying `f` to each element. For multiple collection arguments, | ||
3291 | apply `f` elementwise, and stop when any of them is exhausted. | ||
3292 | |||
3293 | See also [`map!`](@ref), [`foreach`](@ref), [`mapreduce`](@ref), [`mapslices`](@ref), [`zip`](@ref), [`Iterators.map`](@ref). | ||
3294 | |||
3295 | # Examples | ||
3296 | ```jldoctest | ||
3297 | julia> map(x -> x * 2, [1, 2, 3]) | ||
3298 | 3-element Vector{Int64}: | ||
3299 | 2 | ||
3300 | 4 | ||
3301 | 6 | ||
3302 | |||
3303 | julia> map(+, [1, 2, 3], [10, 20, 30, 400, 5000]) | ||
3304 | 3-element Vector{Int64}: | ||
3305 | 11 | ||
3306 | 22 | ||
3307 | 33 | ||
3308 | ``` | ||
3309 | """ | ||
3310 | map(f, A) = collect(Generator(f,A)) # default to returning an Array for `map` on general iterators | ||
3311 | |||
3312 | map(f, ::AbstractDict) = error("map is not defined on dictionaries") | ||
3313 | map(f, ::AbstractSet) = error("map is not defined on sets") | ||
3314 | |||
3315 | ## 2 argument | ||
3316 | function map!(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) where F | ||
3317 | for (i, j, k) in zip(eachindex(dest), eachindex(A), eachindex(B)) | ||
3318 | @inbounds a, b = A[j], B[k] | ||
3319 | val = f(a, b) | ||
3320 | @inbounds dest[i] = val | ||
3321 | end | ||
3322 | return dest | ||
3323 | end | ||
3324 | |||
3325 | ## N argument | ||
3326 | |||
3327 | @inline ith_all(i, ::Tuple{}) = () | ||
3328 | function ith_all(i, as) | ||
3329 | @_propagate_inbounds_meta | ||
3330 | return (as[1][i], ith_all(i, tail(as))...) | ||
3331 | end | ||
3332 | |||
3333 | function map_n!(f::F, dest::AbstractArray, As) where F | ||
3334 | idxs1 = LinearIndices(As[1]) | ||
3335 | @boundscheck LinearIndices(dest) == idxs1 && all(x -> LinearIndices(x) == idxs1, As) | ||
3336 | for i = idxs1 | ||
3337 | @inbounds I = ith_all(i, As) | ||
3338 | val = f(I...) | ||
3339 | @inbounds dest[i] = val | ||
3340 | end | ||
3341 | return dest | ||
3342 | end | ||
3343 | |||
3344 | """ | ||
3345 | map!(function, destination, collection...) | ||
3346 | |||
3347 | Like [`map`](@ref), but stores the result in `destination` rather than a new | ||
3348 | collection. `destination` must be at least as large as the smallest collection. | ||
3349 | |||
3350 | $(_DOCS_ALIASING_WARNING) | ||
3351 | |||
3352 | See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). | ||
3353 | |||
3354 | # Examples | ||
3355 | ```jldoctest | ||
3356 | julia> a = zeros(3); | ||
3357 | |||
3358 | julia> map!(x -> x * 2, a, [1, 2, 3]); | ||
3359 | |||
3360 | julia> a | ||
3361 | 3-element Vector{Float64}: | ||
3362 | 2.0 | ||
3363 | 4.0 | ||
3364 | 6.0 | ||
3365 | |||
3366 | julia> map!(+, zeros(Int, 5), 100:999, 1:3) | ||
3367 | 5-element Vector{$(Int)}: | ||
3368 | 101 | ||
3369 | 103 | ||
3370 | 105 | ||
3371 | 0 | ||
3372 | 0 | ||
3373 | ``` | ||
3374 | """ | ||
3375 | function map!(f::F, dest::AbstractArray, As::AbstractArray...) where {F} | ||
3376 | isempty(As) && throw(ArgumentError( | ||
3377 | """map! requires at least one "source" argument""")) | ||
3378 | map_n!(f, dest, As) | ||
3379 | end | ||
3380 | |||
3381 | """ | ||
3382 | map(f, A::AbstractArray...) -> N-array | ||
3383 | |||
3384 | When acting on multi-dimensional arrays of the same [`ndims`](@ref), | ||
3385 | they must all have the same [`axes`](@ref), and the answer will too. | ||
3386 | |||
3387 | See also [`broadcast`](@ref), which allows mismatched sizes. | ||
3388 | |||
3389 | # Examples | ||
3390 | ``` | ||
3391 | julia> map(//, [1 2; 3 4], [4 3; 2 1]) | ||
3392 | 2×2 Matrix{Rational{$Int}}: | ||
3393 | 1//4 2//3 | ||
3394 | 3//2 4//1 | ||
3395 | |||
3396 | julia> map(+, [1 2; 3 4], zeros(2,1)) | ||
3397 | ERROR: DimensionMismatch | ||
3398 | |||
3399 | julia> map(+, [1 2; 3 4], [1,10,100,1000], zeros(3,1)) # iterates until 3rd is exhausted | ||
3400 | 3-element Vector{Float64}: | ||
3401 | 2.0 | ||
3402 | 13.0 | ||
3403 | 102.0 | ||
3404 | ``` | ||
3405 | """ | ||
3406 | map(f, iters...) = collect(Generator(f, iters...)) | ||
3407 | |||
3408 | # multi-item push!, pushfirst! (built on top of type-specific 1-item version) | ||
3409 | # (note: must not cause a dispatch loop when 1-item case is not defined) | ||
3410 | push!(A, a, b) = push!(push!(A, a), b) | ||
3411 | push!(A, a, b, c...) = push!(push!(A, a, b), c...) | ||
3412 | pushfirst!(A, a, b) = pushfirst!(pushfirst!(A, b), a) | ||
3413 | pushfirst!(A, a, b, c...) = pushfirst!(pushfirst!(A, c...), a, b) | ||
3414 | |||
3415 | ## hashing AbstractArray ## | ||
3416 | |||
3417 | const hash_abstractarray_seed = UInt === UInt64 ? 0x7e2d6fb6448beb77 : 0xd4514ce5 | ||
3418 | function hash(A::AbstractArray, h::UInt) | ||
3419 | h += hash_abstractarray_seed | ||
3420 | # Axes are themselves AbstractArrays, so hashing them directly would stack overflow | ||
3421 | # Instead hash the tuple of firsts and lasts along each dimension | ||
3422 | h = hash(map(first, axes(A)), h) | ||
3423 | h = hash(map(last, axes(A)), h) | ||
3424 | |||
3425 | # For short arrays, it's not worth doing anything complicated | ||
3426 | if length(A) < 8192 | ||
3427 | for x in A | ||
3428 | h = hash(x, h) | ||
3429 | end | ||
3430 | return h | ||
3431 | end | ||
3432 | |||
3433 | # Goal: Hash approximately log(N) entries with a higher density of hashed elements | ||
3434 | # weighted towards the end and special consideration for repeated values. Colliding | ||
3435 | # hashes will often subsequently be compared by equality -- and equality between arrays | ||
3436 | # works elementwise forwards and is short-circuiting. This means that a collision | ||
3437 | # between arrays that differ by elements at the beginning is cheaper than one where the | ||
3438 | # difference is towards the end. Furthermore, choosing `log(N)` arbitrary entries from a | ||
3439 | # sparse array will likely only choose the same element repeatedly (zero in this case). | ||
3440 | |||
3441 | # To achieve this, we work backwards, starting by hashing the last element of the | ||
3442 | # array. After hashing each element, we skip `fibskip` elements, where `fibskip` | ||
3443 | # is pulled from the Fibonacci sequence -- Fibonacci was chosen as a simple | ||
3444 | # ~O(log(N)) algorithm that ensures we don't hit a common divisor of a dimension | ||
3445 | # and only end up hashing one slice of the array (as might happen with powers of | ||
3446 | # two). Finally, we find the next distinct value from the one we just hashed. | ||
3447 | |||
3448 | # This is a little tricky since skipping an integer number of values inherently works | ||
3449 | # with linear indices, but `findprev` uses `keys`. Hoist out the conversion "maps": | ||
3450 | ks = keys(A) | ||
3451 | key_to_linear = LinearIndices(ks) # Index into this map to compute the linear index | ||
3452 | linear_to_key = vec(ks) # And vice-versa | ||
3453 | |||
3454 | # Start at the last index | ||
3455 | keyidx = last(ks) | ||
3456 | linidx = key_to_linear[keyidx] | ||
3457 | fibskip = prevfibskip = oneunit(linidx) | ||
3458 | first_linear = first(LinearIndices(linear_to_key)) | ||
3459 | n = 0 | ||
3460 | while true | ||
3461 | n += 1 | ||
3462 | # Hash the element | ||
3463 | elt = A[keyidx] | ||
3464 | h = hash(keyidx=>elt, h) | ||
3465 | |||
3466 | # Skip backwards a Fibonacci number of indices -- this is a linear index operation | ||
3467 | linidx = key_to_linear[keyidx] | ||
3468 | linidx < fibskip + first_linear && break | ||
3469 | linidx -= fibskip | ||
3470 | keyidx = linear_to_key[linidx] | ||
3471 | |||
3472 | # Only increase the Fibonacci skip once every N iterations. This was chosen | ||
3473 | # to be big enough that all elements of small arrays get hashed while | ||
3474 | # obscenely large arrays are still tractable. With a choice of N=4096, an | ||
3475 | # entirely-distinct 8000-element array will have ~75% of its elements hashed, | ||
3476 | # with every other element hashed in the first half of the array. At the same | ||
3477 | # time, hashing a `typemax(Int64)`-length Float64 range takes about a second. | ||
3478 | if rem(n, 4096) == 0 | ||
3479 | fibskip, prevfibskip = fibskip + prevfibskip, fibskip | ||
3480 | end | ||
3481 | |||
3482 | # Find a key index with a value distinct from `elt` -- might be `keyidx` itself | ||
3483 | keyidx = findprev(!isequal(elt), A, keyidx) | ||
3484 | keyidx === nothing && break | ||
3485 | end | ||
3486 | |||
3487 | return h | ||
3488 | end | ||
3489 | |||
3490 | # The semantics of `collect` are weird. Better to write our own | ||
3491 | function rest(a::AbstractArray{T}, state...) where {T} | ||
3492 | v = Vector{T}(undef, 0) | ||
3493 | # assume only very few items are taken from the front | ||
3494 | sizehint!(v, length(a)) | ||
3495 | return foldl(push!, Iterators.rest(a, state...), init=v) | ||
3496 | end | ||
3497 | |||
3498 | |||
3499 | ## keepat! ## | ||
3500 | |||
3501 | # NOTE: since these use `@inbounds`, they are actually only intended for Vector and BitVector | ||
3502 | |||
3503 | function _keepat!(a::AbstractVector, inds) | ||
3504 | local prev | ||
3505 | i = firstindex(a) | ||
3506 | for k in inds | ||
3507 | if @isdefined(prev) | ||
3508 | prev < k || throw(ArgumentError("indices must be unique and sorted")) | ||
3509 | end | ||
3510 | ak = a[k] # must happen even when i==k for bounds checking | ||
3511 | if i != k | ||
3512 | @inbounds a[i] = ak # k > i, so a[i] is inbounds | ||
3513 | end | ||
3514 | prev = k | ||
3515 | i = nextind(a, i) | ||
3516 | end | ||
3517 | deleteat!(a, i:lastindex(a)) | ||
3518 | return a | ||
3519 | end | ||
3520 | |||
3521 | function _keepat!(a::AbstractVector, m::AbstractVector{Bool}) | ||
3522 | length(m) == length(a) || throw(BoundsError(a, m)) | ||
3523 | j = firstindex(a) | ||
3524 | for i in eachindex(a, m) | ||
3525 | @inbounds begin | ||
3526 | if m[i] | ||
3527 | i == j || (a[j] = a[i]) | ||
3528 | j = nextind(a, j) | ||
3529 | end | ||
3530 | end | ||
3531 | end | ||
3532 | deleteat!(a, j:lastindex(a)) | ||
3533 | end | ||
3534 | |||
3535 | ## 1-d circshift ## | ||
3536 | function circshift!(a::AbstractVector, shift::Integer) | ||
3537 | n = length(a) | ||
3538 | n == 0 && return | ||
3539 | shift = mod(shift, n) | ||
3540 | shift == 0 && return | ||
3541 | l = lastindex(a) | ||
3542 | reverse!(a, firstindex(a), l-shift) | ||
3543 | reverse!(a, l-shift+1, lastindex(a)) | ||
3544 | reverse!(a) | ||
3545 | return a | ||
3546 | end |