• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38162

06 Aug 2025 08:25PM UTC coverage: 25.688% (-43.6%) from 69.336%
#38162

push

local

web-flow
fix runtime cglobal builtin function implementation (#59210)

This had failed to be updated for the LazyLibrary changes to codegen.

12976 of 50513 relevant lines covered (25.69%)

676965.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.95
/base/permuteddimsarray.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
module PermutedDimsArrays
4

5
import Base: permutedims, permutedims!
6
export PermutedDimsArray
7

8
# Some day we will want storage-order-aware iteration, so put perm in the parameters
9
struct PermutedDimsArray{T,N,perm,iperm,AA<:AbstractArray} <: AbstractArray{T,N}
10
    parent::AA
11

12
    function PermutedDimsArray{T,N,perm,iperm,AA}(data::AA) where {T,N,perm,iperm,AA<:AbstractArray}
×
13
        (isa(perm, NTuple{N,Int}) && isa(iperm, NTuple{N,Int})) || error("perm and iperm must both be NTuple{$N,Int}")
×
14
        isperm(perm) || throw(ArgumentError(string(perm, " is not a valid permutation of dimensions 1:", N)))
×
15
        all(d->iperm[perm[d]]==d, 1:N) || throw(ArgumentError(string(perm, " and ", iperm, " must be inverses")))
×
16
        new(data)
×
17
    end
18
end
19

20
"""
21
    PermutedDimsArray(A, perm) -> B
22

23
Given an AbstractArray `A`, create a view `B` such that the
24
dimensions appear to be permuted. Similar to `permutedims`, except
25
that no copying occurs (`B` shares storage with `A`).
26

27
See also [`permutedims`](@ref), [`invperm`](@ref).
28

29
# Examples
30
```jldoctest
31
julia> A = rand(3,5,4);
32

33
julia> B = PermutedDimsArray(A, (3,1,2));
34

35
julia> size(B)
36
(4, 3, 5)
37

38
julia> B[3,1,2] == A[1,2,3]
39
true
40
```
41
"""
42
Base.@constprop :aggressive function PermutedDimsArray(data::AbstractArray{T,N}, perm) where {T,N}
×
43
    length(perm) == N || throw(ArgumentError(string(perm, " is not a valid permutation of dimensions 1:", N)))
×
44
    iperm = invperm(perm)
×
45
    PermutedDimsArray{T,N,(perm...,),(iperm...,),typeof(data)}(data)
×
46
end
47

48
Base.parent(A::PermutedDimsArray) = A.parent
×
49
Base.size(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(size(parent(A)), perm)
×
50
Base.axes(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(axes(parent(A)), perm)
×
51
Base.has_offset_axes(A::PermutedDimsArray) = Base.has_offset_axes(A.parent)
×
52
Base.similar(A::PermutedDimsArray, T::Type, dims::Base.Dims) = similar(parent(A), T, dims)
×
53
Base.cconvert(::Type{Ptr{T}}, A::PermutedDimsArray{T}) where {T} = Base.cconvert(Ptr{T}, parent(A))
×
54

55
# It's OK to return a pointer to the first element, and indeed quite
56
# useful for wrapping C routines that require a different storage
57
# order than used by Julia. But for an array with unconventional
58
# storage order, a linear offset is ambiguous---is it a memory offset
59
# or a linear index?
60
Base.pointer(A::PermutedDimsArray, i::Integer) = throw(ArgumentError("pointer(A, i) is deliberately unsupported for PermutedDimsArray"))
×
61

62
function Base.strides(A::PermutedDimsArray{T,N,perm}) where {T,N,perm}
×
63
    s = strides(parent(A))
×
64
    ntuple(d->s[perm[d]], Val(N))
×
65
end
66
Base.elsize(::Type{<:PermutedDimsArray{<:Any, <:Any, <:Any, <:Any, P}}) where {P} = Base.elsize(P)
×
67

68
@inline function Base.getindex(A::PermutedDimsArray{T,N,perm,iperm}, I::Vararg{Int,N}) where {T,N,perm,iperm}
×
69
    @boundscheck checkbounds(A, I...)
×
70
    @inbounds val = getindex(A.parent, genperm(I, iperm)...)
×
71
    val
×
72
end
73
@inline function Base.setindex!(A::PermutedDimsArray{T,N,perm,iperm}, val, I::Vararg{Int,N}) where {T,N,perm,iperm}
×
74
    @boundscheck checkbounds(A, I...)
×
75
    @inbounds setindex!(A.parent, val, genperm(I, iperm)...)
×
76
    val
×
77
end
78

79
function Base.isassigned(A::PermutedDimsArray{T,N,perm,iperm}, I::Vararg{Int,N}) where {T,N,perm,iperm}
×
80
    @boundscheck checkbounds(Bool, A, I...) || return false
×
81
    @inbounds x = isassigned(A.parent, genperm(I, iperm)...)
×
82
    x
×
83
end
84

85
@inline genperm(I::NTuple{N,Any}, perm::Dims{N}) where {N} = ntuple(d -> I[perm[d]], Val(N))
×
86
@inline genperm(I, perm::AbstractVector{Int}) = genperm(I, (perm...,))
×
87

88
"""
89
    permutedims(A::AbstractArray, perm)
90
    permutedims(A::AbstractMatrix)
91

92
Permute the dimensions (axes) of array `A`. `perm` is a tuple or vector of `ndims(A)` integers
93
specifying the permutation.
94

95
If `A` is a 2d array ([`AbstractMatrix`](@ref)), then
96
`perm` defaults to `(2,1)`, swapping the two axes of `A` (the rows and columns
97
of the matrix).   This differs from [`transpose`](@ref) in that the
98
operation is not recursive, which is especially useful for arrays of non-numeric values
99
(where the recursive `transpose` would throw an error) and/or 2d arrays that do not represent
100
linear operators.
101

102
For 1d arrays, see [`permutedims(v::AbstractVector)`](@ref), which returns a 1-row “matrix”.
103

104
See also [`permutedims!`](@ref), [`PermutedDimsArray`](@ref), [`transpose`](@ref), [`invperm`](@ref).
105

106
# Examples
107

108
## 2d arrays:
109
Unlike `transpose`, `permutedims` can be used to swap rows and columns of 2d arrays of
110
arbitrary non-numeric elements, such as strings:
111
```jldoctest
112
julia> A = ["a" "b" "c"
113
            "d" "e" "f"]
114
2×3 Matrix{String}:
115
 "a"  "b"  "c"
116
 "d"  "e"  "f"
117

118
julia> permutedims(A)
119
3×2 Matrix{String}:
120
 "a"  "d"
121
 "b"  "e"
122
 "c"  "f"
123
```
124
And `permutedims` produces results that differ from `transpose`
125
for matrices whose elements are themselves numeric matrices:
126
```jldoctest; setup = :(using LinearAlgebra)
127
julia> a = [1 2; 3 4];
128

129
julia> b = [5 6; 7 8];
130

131
julia> c = [9 10; 11 12];
132

133
julia> d = [13 14; 15 16];
134

135
julia> X = [[a] [b]; [c] [d]]
136
2×2 Matrix{Matrix{Int64}}:
137
 [1 2; 3 4]     [5 6; 7 8]
138
 [9 10; 11 12]  [13 14; 15 16]
139

140
julia> permutedims(X)
141
2×2 Matrix{Matrix{Int64}}:
142
 [1 2; 3 4]  [9 10; 11 12]
143
 [5 6; 7 8]  [13 14; 15 16]
144

145
julia> transpose(X)
146
2×2 transpose(::Matrix{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}:
147
 [1 3; 2 4]  [9 11; 10 12]
148
 [5 7; 6 8]  [13 15; 14 16]
149
```
150

151
## Multi-dimensional arrays
152
```jldoctest
153
julia> A = reshape(Vector(1:8), (2,2,2))
154
2×2×2 Array{Int64, 3}:
155
[:, :, 1] =
156
 1  3
157
 2  4
158

159
[:, :, 2] =
160
 5  7
161
 6  8
162

163
julia> perm = (3, 1, 2); # put the last dimension first
164

165
julia> B = permutedims(A, perm)
166
2×2×2 Array{Int64, 3}:
167
[:, :, 1] =
168
 1  2
169
 5  6
170

171
[:, :, 2] =
172
 3  4
173
 7  8
174

175
julia> A == permutedims(B, invperm(perm)) # the inverse permutation
176
true
177
```
178

179
For each dimension `i` of `B = permutedims(A, perm)`, its corresponding dimension of `A`
180
will be `perm[i]`. This means the equality `size(B, i) == size(A, perm[i])` holds.
181

182
```jldoctest
183
julia> A = randn(5, 7, 11, 13);
184

185
julia> perm = [4, 1, 3, 2];
186

187
julia> B = permutedims(A, perm);
188

189
julia> size(B)
190
(13, 5, 11, 7)
191

192
julia> size(A)[perm] == ans
193
true
194
```
195
"""
196
function permutedims(A::AbstractArray, perm)
×
197
    dest = similar(A, genperm(axes(A), perm))
×
198
    permutedims!(dest, A, perm)
×
199
end
200

201
permutedims(A::AbstractMatrix) = permutedims(A, (2,1))
×
202

203
"""
204
    permutedims(v::AbstractVector)
205

206
Reshape vector `v` into a `1 × length(v)` row matrix.
207
Differs from [`transpose`](@ref) in that
208
the operation is not recursive, which is especially useful for arrays of non-numeric values
209
(where the recursive `transpose` might throw an error).
210

211
# Examples
212
Unlike `transpose`, `permutedims` can be used on vectors of
213
arbitrary non-numeric elements, such as strings:
214
```jldoctest
215
julia> permutedims(["a", "b", "c"])
216
1×3 Matrix{String}:
217
 "a"  "b"  "c"
218
```
219
For vectors of numbers, `permutedims(v)` works much like `transpose(v)`
220
except that the return type differs (it uses [`reshape`](@ref)
221
rather than a `LinearAlgebra.Transpose` view, though both
222
share memory with the original array `v`):
223
```jldoctest; setup = :(using LinearAlgebra)
224
julia> v = [1, 2, 3, 4]
225
4-element Vector{Int64}:
226
 1
227
 2
228
 3
229
 4
230

231
julia> p = permutedims(v)
232
1×4 Matrix{Int64}:
233
 1  2  3  4
234

235
julia> r = transpose(v)
236
1×4 transpose(::Vector{Int64}) with eltype Int64:
237
 1  2  3  4
238

239
julia> p == r
240
true
241

242
julia> typeof(r)
243
Transpose{Int64, Vector{Int64}}
244

245
julia> p[1] = 5; r[2] = 6; # mutating p or r also changes v
246

247
julia> v # shares memory with both p and r
248
4-element Vector{Int64}:
249
 5
250
 6
251
 3
252
 4
253
```
254
However, `permutedims` produces results that differ from `transpose`
255
for vectors whose elements are themselves numeric matrices:
256
```jldoctest; setup = :(using LinearAlgebra)
257
julia> V = [[[1 2; 3 4]]; [[5 6; 7 8]]]
258
2-element Vector{Matrix{Int64}}:
259
 [1 2; 3 4]
260
 [5 6; 7 8]
261

262
julia> permutedims(V)
263
1×2 Matrix{Matrix{Int64}}:
264
 [1 2; 3 4]  [5 6; 7 8]
265

266
julia> transpose(V)
267
1×2 transpose(::Vector{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}:
268
 [1 3; 2 4]  [5 7; 6 8]
269
```
270
"""
271
permutedims(v::AbstractVector) = reshape(v, (1, length(v)))
123✔
272

273
"""
274
    permutedims!(dest, src, perm)
275

276
Permute the dimensions of array `src` and store the result in the array `dest`. `perm` is a
277
vector specifying a permutation of length `ndims(src)`. The preallocated array `dest` should
278
have `size(dest) == size(src)[perm]` and is completely overwritten. No in-place permutation
279
is supported and unexpected results will happen if `src` and `dest` have overlapping memory
280
regions.
281

282
See also [`permutedims`](@ref).
283
"""
284
function permutedims!(dest, src::AbstractArray, perm)
×
285
    Base.checkdims_perm(axes(dest), axes(src), perm)
×
286
    P = PermutedDimsArray(dest, invperm(perm))
×
287
    _copy!(P, src)
×
288
    return dest
×
289
end
290

291
function Base.copyto!(dest::PermutedDimsArray{T,N}, src::AbstractArray{T,N}) where {T,N}
×
292
    checkbounds(dest, axes(src)...)
×
293
    _copy!(dest, src)
×
294
end
295
Base.copyto!(dest::PermutedDimsArray, src::AbstractArray) = _copy!(dest, src)
×
296

297
function _copy!(P::PermutedDimsArray{T,N,perm}, src) where {T,N,perm}
×
298
    # If dest/src are "close to dense," then it pays to be cache-friendly.
299
    # Determine the first permuted dimension
300
    d = 0  # d+1 will hold the first permuted dimension of src
×
301
    while d < ndims(src) && perm[d+1] == d+1
×
302
        d += 1
×
303
    end
×
304
    if d == ndims(src)
×
305
        copyto!(parent(P), src) # it's not permuted
×
306
    else
307
        R1 = CartesianIndices(axes(src)[1:d])
×
308
        d1 = findfirst(isequal(d+1), perm)::Int  # first permuted dim of dest
×
309
        R2 = CartesianIndices(axes(src)[d+2:d1-1])
×
310
        R3 = CartesianIndices(axes(src)[d1+1:end])
×
311
        _permutedims!(P, src, R1, R2, R3, d+1, d1)
×
312
    end
313
    return P
×
314
end
315

316
@noinline function _permutedims!(P::PermutedDimsArray, src, R1::CartesianIndices{0}, R2, R3, ds, dp)
×
317
    ip, is = axes(src, dp), axes(src, ds)
×
318
    for jo in first(ip):8:last(ip), io in first(is):8:last(is)
×
319
        for I3 in R3, I2 in R2
×
320
            for j in jo:min(jo+7, last(ip))
×
321
                for i in io:min(io+7, last(is))
×
322
                    @inbounds P[i, I2, j, I3] = src[i, I2, j, I3]
×
323
                end
×
324
            end
×
325
        end
×
326
    end
×
327
    P
×
328
end
329

330
@noinline function _permutedims!(P::PermutedDimsArray, src, R1, R2, R3, ds, dp)
×
331
    ip, is = axes(src, dp), axes(src, ds)
×
332
    for jo in first(ip):8:last(ip), io in first(is):8:last(is)
×
333
        for I3 in R3, I2 in R2
×
334
            for j in jo:min(jo+7, last(ip))
×
335
                for i in io:min(io+7, last(is))
×
336
                    for I1 in R1
×
337
                        @inbounds P[I1, i, I2, j, I3] = src[I1, i, I2, j, I3]
×
338
                    end
×
339
                end
×
340
            end
×
341
        end
×
342
    end
×
343
    P
×
344
end
345

346
const CommutativeOps = Union{typeof(+),typeof(Base.add_sum),typeof(min),typeof(max),typeof(Base._extrema_rf),typeof(|),typeof(&)}
347

348
function Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::PermutedDimsArray, dims::Colon)
×
349
    Base._mapreduce_dim(f, op, init, parent(A), dims)
×
350
end
351
function Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(Base.mul_prod),typeof(*)}, init::Base._InitialValue, A::PermutedDimsArray{<:Union{Real,Complex}}, dims::Colon)
×
352
    Base._mapreduce_dim(f, op, init, parent(A), dims)
×
353
end
354

355
function Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray{T,N}, A::PermutedDimsArray{S,N,perm,iperm}) where {T,S,N,perm,iperm}
×
356
    C = PermutedDimsArray{T,N,iperm,perm,typeof(B)}(B) # make the inverse permutation for the output
×
357
    Base.mapreducedim!(f, op, C, parent(A))
×
358
    B
×
359
end
360
function Base.mapreducedim!(f::typeof(identity), op::Union{typeof(Base.mul_prod),typeof(*)}, B::AbstractArray{T,N}, A::PermutedDimsArray{<:Union{Real,Complex},N,perm,iperm}) where {T,N,perm,iperm}
×
361
    C = PermutedDimsArray{T,N,iperm,perm,typeof(B)}(B) # make the inverse permutation for the output
×
362
    Base.mapreducedim!(f, op, C, parent(A))
×
363
    B
×
364
end
365

366
function Base.showarg(io::IO, A::PermutedDimsArray{T,N,perm}, toplevel) where {T,N,perm}
×
367
    print(io, "PermutedDimsArray(")
×
368
    Base.showarg(io, parent(A), false)
×
369
    print(io, ", ", perm, ')')
×
370
    toplevel && print(io, " with eltype ", eltype(A))
×
371
    return nothing
×
372
end
373

374
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc