Skip to content

Commit b7cacf7

Browse files
mbaumantimholy
andauthored
Strided array doc: Remove reference to getindex (#26150)
* Strided array doc: Remove reference to getindex As noticed by Jutho, we don't assume that `getindex(A, i)` is the same as accessing the `i`th element as computed by the strides. * Rewrite with motivating examples... instead of consise theoretical arithmetic. * Emphasize the distinction from linear indexing Co-authored-by: Tim Holy <[email protected]>
1 parent 2253843 commit b7cacf7

File tree

1 file changed

+66
-51
lines changed

1 file changed

+66
-51
lines changed

doc/src/manual/arrays.md

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,57 +1000,72 @@ create a `SubArray` view instead.
10001000
They can be used similarly to `Array{Bool}` arrays (which store one byte per boolean value),
10011001
and can be converted to/from the latter via `Array(bitarray)` and `BitArray(array)`, respectively.
10021002

1003-
A "strided" array is stored in memory with elements laid out in regular offsets such that
1004-
an instance with a supported `isbits` element type can be passed to
1005-
external C and Fortran functions that expect this memory layout. Strided arrays
1006-
must define a [`strides(A)`](@ref) method that returns a tuple of "strides" for each dimension; a
1007-
provided [`stride(A,k)`](@ref) method accesses the `k`th element within this tuple. Increasing the
1008-
index of dimension `k` by `1` should increase the index `i` of [`getindex(A,i)`](@ref) by
1009-
[`stride(A,k)`](@ref). If a pointer conversion method [`Base.unsafe_convert(Ptr{T}, A)`](@ref) is
1010-
provided, the memory layout must correspond in the same way to these strides. `DenseArray` is a
1011-
very specific example of a strided array where the elements are arranged contiguously, thus it
1012-
provides its subtypes with the appropriate definition of `strides`. More concrete examples
1013-
can be found within the [interface guide for strided arrays](@ref man-interface-strided-arrays).
1014-
[`StridedVector`](@ref) and [`StridedMatrix`](@ref) are convenient aliases for many of the builtin array types that
1015-
are considered strided arrays, allowing them to dispatch to select specialized implementations that
1016-
call highly tuned and optimized BLAS and LAPACK functions using just the pointer and strides.
1017-
1018-
The following example computes the QR decomposition of a small section of a larger array, without
1019-
creating any temporaries, and by calling the appropriate LAPACK function with the right leading
1020-
dimension size and stride parameters.
1003+
An array is "strided" if it is stored in memory with well-defined spacings (strides) between
1004+
its elements. A strided array with a supported element type may be passed to an external
1005+
(non-Julia) library like BLAS or LAPACK by simply passing its [`pointer`](@ref) and the
1006+
stride for each dimension. The [`stride(A, d)`](@ref) is the distance between elements along
1007+
dimension `d`. For example, the builtin `Array` returned by `rand(5,7,2)` has its elements
1008+
arranged contiguously in column major order. This means that the stride of the first
1009+
dimension — the spacing between elements in the same column — is `1`:
10211010

10221011
```julia-repl
1023-
julia> a = rand(10, 10)
1024-
10×10 Array{Float64,2}:
1025-
0.517515 0.0348206 0.749042 0.0979679 … 0.75984 0.950481 0.579513
1026-
0.901092 0.873479 0.134533 0.0697848 0.0586695 0.193254 0.726898
1027-
0.976808 0.0901881 0.208332 0.920358 0.288535 0.705941 0.337137
1028-
0.657127 0.0317896 0.772837 0.534457 0.0966037 0.700694 0.675999
1029-
0.471777 0.144969 0.0718405 0.0827916 0.527233 0.173132 0.694304
1030-
0.160872 0.455168 0.489254 0.827851 … 0.62226 0.0995456 0.946522
1031-
0.291857 0.769492 0.68043 0.629461 0.727558 0.910796 0.834837
1032-
0.775774 0.700731 0.700177 0.0126213 0.00822304 0.327502 0.955181
1033-
0.9715 0.64354 0.848441 0.241474 0.591611 0.792573 0.194357
1034-
0.646596 0.575456 0.0995212 0.038517 0.709233 0.477657 0.0507231
1035-
1036-
julia> b = view(a, 2:2:8,2:2:4)
1037-
4×2 view(::Array{Float64,2}, 2:2:8, 2:2:4) with eltype Float64:
1038-
0.873479 0.0697848
1039-
0.0317896 0.534457
1040-
0.455168 0.827851
1041-
0.700731 0.0126213
1042-
1043-
julia> (q, r) = qr(b);
1044-
1045-
julia> q
1046-
4×4 LinearAlgebra.QRCompactWYQ{Float64,Array{Float64,2}}:
1047-
-0.722358 0.227524 -0.247784 -0.604181
1048-
-0.0262896 -0.575919 -0.804227 0.144377
1049-
-0.376419 -0.75072 0.540177 -0.0541979
1050-
-0.579497 0.230151 -0.00552346 0.781782
1051-
1052-
julia> r
1053-
2×2 Array{Float64,2}:
1054-
-1.20921 -0.383393
1055-
0.0 -0.910506
1012+
julia> A = rand(5,7,2);
1013+
1014+
julia> stride(A,1)
1015+
1
1016+
```
1017+
1018+
The stride of the second dimension is the spacing between elements in the same row, skipping
1019+
as many elements as there are in a single column (`5`). Similarly, jumping between the two
1020+
"pages" (in the third dimension) requires skipping `5*7 == 35` elements. The [`strides`](@ref)
1021+
of this array is the tuple of these three numbers together:
1022+
1023+
```julia-repl
1024+
julia> strides(A)
1025+
(1, 5, 35)
10561026
```
1027+
1028+
In this particular case, the number of elements skipped _in memory_ matches the number of
1029+
_linear indices_ skipped. This is only the case for contiguous arrays like `Array` (and
1030+
other `DenseArray` subtypes) and is not true in general. Views with range indices are a good
1031+
example of _non-contiguous_ strided arrays; consider `V = @view A[1:3:4, 2:2:6, 2:-1:1]`.
1032+
This view `V` refers to the same memory as `A` but is skipping and re-arranging some of its
1033+
elements. The stride of the first dimension of `V` is `3` because we're only selecting every
1034+
third row from our original array:
1035+
1036+
```julia-repl
1037+
julia> V = @view A[1:3:4, 2:2:6, 2:-1:1];
1038+
1039+
julia> stride(V, 1)
1040+
3
1041+
```
1042+
1043+
This view is similarly selecting every other column from our original `A` — and thus it
1044+
needs to skip the equivalent of two five-element columns when moving between indices in the
1045+
second dimension:
1046+
1047+
```julia-repl
1048+
julia> stride(V, 2)
1049+
10
1050+
```
1051+
1052+
The third dimension is interesting because its order is reversed! Thus to get from the first
1053+
"page" to the second one it must go _backwards_ in memory, and so its stride in this
1054+
dimension is negative!
1055+
1056+
```julia-repl
1057+
julia> stride(V, 3)
1058+
-35
1059+
```
1060+
1061+
This means that the `pointer` for `V` is actually pointing into the middle of `A`'s memory
1062+
block, and it refers to elements both backwards and forwards in memory. See the
1063+
[interface guide for strided arrays](@ref man-interface-strided-arrays) for more details on
1064+
defining your own strided arrays. [`StridedVector`](@ref) and [`StridedMatrix`](@ref) are
1065+
convenient aliases for many of the builtin array types that are considered strided arrays,
1066+
allowing them to dispatch to select specialized implementations that call highly tuned and
1067+
optimized BLAS and LAPACK functions using just the pointer and strides.
1068+
1069+
It is worth emphasizing that strides are about offsets in memory rather than indexing. If
1070+
you are looking to convert between linear (single-index) indexing and cartesian
1071+
(multi-index) indexing, see [`LinearIndices`](@ref) and [`CartesianIndices`](@ref).

0 commit comments

Comments
 (0)