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

JuliaLang / julia / #37477

pending completion
#37477

push

local

web-flow
Allow external lattice elements to properly union split (#49030)

Currently `MustAlias` is the only lattice element that is allowed
to widen to union types. However, there are others in external
packages. Expand the support we have for this in order to allow
union splitting of lattice elements.

Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com>

26 of 26 new or added lines in 5 files covered. (100.0%)

71476 of 82705 relevant lines covered (86.42%)

34756248.54 hits per line

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

70.3
/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}
271✔
13
        (isa(perm, NTuple{N,Int}) && isa(iperm, NTuple{N,Int})) || error("perm and iperm must both be NTuple{$N,Int}")
271✔
14
        isperm(perm) || throw(ArgumentError(string(perm, " is not a valid permutation of dimensions 1:", N)))
271✔
15
        all(map(d->iperm[perm[d]]==d, 1:N)) || throw(ArgumentError(string(perm, " and ", iperm, " must be inverses")))
933✔
16
        new(data)
271✔
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
function PermutedDimsArray(data::AbstractArray{T,N}, perm) where {T,N}
271✔
43
    length(perm) == N || throw(ArgumentError(string(perm, " is not a valid permutation of dimensions 1:", N)))
271✔
44
    iperm = invperm(perm)
271✔
45
    PermutedDimsArray{T,N,(perm...,),(iperm...,),typeof(data)}(data)
271✔
46
end
47

48
Base.parent(A::PermutedDimsArray) = A.parent
240,960✔
49
Base.size(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(size(parent(A)), perm)
49,694✔
50
Base.axes(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(axes(parent(A)), perm)
137,653✔
51
Base.has_offset_axes(A::PermutedDimsArray) = Base.has_offset_axes(A.parent)
×
52

53
Base.similar(A::PermutedDimsArray, T::Type, dims::Base.Dims) = similar(parent(A), T, dims)
×
54

55
Base.unsafe_convert(::Type{Ptr{T}}, A::PermutedDimsArray{T}) where {T} = Base.unsafe_convert(Ptr{T}, parent(A))
17,980✔
56

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

64
function Base.strides(A::PermutedDimsArray{T,N,perm}) where {T,N,perm}
35,600✔
65
    s = strides(parent(A))
35,600✔
66
    ntuple(d->s[perm[d]], Val(N))
141,270✔
67
end
68
Base.elsize(::Type{<:PermutedDimsArray{<:Any, <:Any, <:Any, <:Any, P}}) where {P} = Base.elsize(P)
35,560✔
69

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

81
@inline genperm(I::NTuple{N,Any}, perm::Dims{N}) where {N} = ntuple(d -> I[perm[d]], Val(N))
1,015,111✔
82
@inline genperm(I, perm::AbstractVector{Int}) = genperm(I, (perm...,))
1✔
83

84
"""
85
    permutedims(A::AbstractArray, perm)
86

87
Permute the dimensions of array `A`. `perm` is a vector or a tuple of length `ndims(A)`
88
specifying the permutation.
89

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

92
# Examples
93
```jldoctest
94
julia> A = reshape(Vector(1:8), (2,2,2))
95
2×2×2 Array{Int64, 3}:
96
[:, :, 1] =
97
 1  3
98
 2  4
99

100
[:, :, 2] =
101
 5  7
102
 6  8
103

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

106
julia> B = permutedims(A, perm)
107
2×2×2 Array{Int64, 3}:
108
[:, :, 1] =
109
 1  2
110
 5  6
111

112
[:, :, 2] =
113
 3  4
114
 7  8
115

116
julia> A == permutedims(B, invperm(perm)) # the inverse permutation
117
true
118
```
119

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

123
```jldoctest
124
julia> A = randn(5, 7, 11, 13);
125

126
julia> perm = [4, 1, 3, 2];
127

128
julia> B = permutedims(A, perm);
129

130
julia> size(B)
131
(13, 5, 11, 7)
132

133
julia> size(A)[perm] == ans
134
true
135
```
136
"""
137
function permutedims(A::AbstractArray, perm)
6✔
138
    dest = similar(A, genperm(axes(A), perm))
10✔
139
    permutedims!(dest, A, perm)
6✔
140
end
141

142
"""
143
    permutedims(m::AbstractMatrix)
144

145
Permute the dimensions of the matrix `m`, by flipping the elements across the diagonal of
146
the matrix. Differs from `LinearAlgebra`'s [`transpose`](@ref) in that the
147
operation is not recursive.
148

149
# Examples
150
```jldoctest; setup = :(using LinearAlgebra)
151
julia> a = [1 2; 3 4];
152

153
julia> b = [5 6; 7 8];
154

155
julia> c = [9 10; 11 12];
156

157
julia> d = [13 14; 15 16];
158

159
julia> X = [[a] [b]; [c] [d]]
160
2×2 Matrix{Matrix{Int64}}:
161
 [1 2; 3 4]     [5 6; 7 8]
162
 [9 10; 11 12]  [13 14; 15 16]
163

164
julia> permutedims(X)
165
2×2 Matrix{Matrix{Int64}}:
166
 [1 2; 3 4]  [9 10; 11 12]
167
 [5 6; 7 8]  [13 14; 15 16]
168

169
julia> transpose(X)
170
2×2 transpose(::Matrix{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}:
171
 [1 3; 2 4]  [9 11; 10 12]
172
 [5 7; 6 8]  [13 15; 14 16]
173
```
174
"""
175
permutedims(A::AbstractMatrix) = permutedims(A, (2,1))
3✔
176

177
"""
178
    permutedims(v::AbstractVector)
179

180
Reshape vector `v` into a `1 × length(v)` row matrix.
181
Differs from `LinearAlgebra`'s [`transpose`](@ref) in that
182
the operation is not recursive.
183

184
# Examples
185
```jldoctest; setup = :(using LinearAlgebra)
186
julia> permutedims([1, 2, 3, 4])
187
1×4 Matrix{Int64}:
188
 1  2  3  4
189

190
julia> V = [[[1 2; 3 4]]; [[5 6; 7 8]]]
191
2-element Vector{Matrix{Int64}}:
192
 [1 2; 3 4]
193
 [5 6; 7 8]
194

195
julia> permutedims(V)
196
1×2 Matrix{Matrix{Int64}}:
197
 [1 2; 3 4]  [5 6; 7 8]
198

199
julia> transpose(V)
200
1×2 transpose(::Vector{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}:
201
 [1 3; 2 4]  [5 7; 6 8]
202
```
203
"""
204
permutedims(v::AbstractVector) = reshape(v, (1, length(v)))
122✔
205

206
"""
207
    permutedims!(dest, src, perm)
208

209
Permute the dimensions of array `src` and store the result in the array `dest`. `perm` is a
210
vector specifying a permutation of length `ndims(src)`. The preallocated array `dest` should
211
have `size(dest) == size(src)[perm]` and is completely overwritten. No in-place permutation
212
is supported and unexpected results will happen if `src` and `dest` have overlapping memory
213
regions.
214

215
See also [`permutedims`](@ref).
216
"""
217
function permutedims!(dest, src::AbstractArray, perm)
4✔
218
    Base.checkdims_perm(dest, src, perm)
4✔
219
    P = PermutedDimsArray(dest, invperm(perm))
4✔
220
    _copy!(P, src)
4✔
221
    return dest
4✔
222
end
223

224
function Base.copyto!(dest::PermutedDimsArray{T,N}, src::AbstractArray{T,N}) where {T,N}
×
225
    checkbounds(dest, axes(src)...)
×
226
    _copy!(dest, src)
×
227
end
228
Base.copyto!(dest::PermutedDimsArray, src::AbstractArray) = _copy!(dest, src)
×
229

230
function _copy!(P::PermutedDimsArray{T,N,perm}, src) where {T,N,perm}
4✔
231
    # If dest/src are "close to dense," then it pays to be cache-friendly.
232
    # Determine the first permuted dimension
233
    d = 0  # d+1 will hold the first permuted dimension of src
4✔
234
    while d < ndims(src) && perm[d+1] == d+1
6✔
235
        d += 1
2✔
236
    end
2✔
237
    if d == ndims(src)
4✔
238
        copyto!(parent(P), src) # it's not permuted
2✔
239
    else
240
        R1 = CartesianIndices(axes(src)[1:d])
3✔
241
        d1 = findfirst(isequal(d+1), perm)::Int  # first permuted dim of dest
3✔
242
        R2 = CartesianIndices(axes(src)[d+2:d1-1])
3✔
243
        R3 = CartesianIndices(axes(src)[d1+1:end])
3✔
244
        _permutedims!(P, src, R1, R2, R3, d+1, d1)
3✔
245
    end
246
    return P
4✔
247
end
248

249
@noinline function _permutedims!(P::PermutedDimsArray, src, R1::CartesianIndices{0}, R2, R3, ds, dp)
3✔
250
    ip, is = axes(src, dp), axes(src, ds)
3✔
251
    for jo in first(ip):8:last(ip), io in first(is):8:last(is)
6✔
252
        for I3 in R3, I2 in R2
3✔
253
            for j in jo:min(jo+7, last(ip))
6✔
254
                for i in io:min(io+7, last(is))
24✔
255
                    @inbounds P[i, I2, j, I3] = src[i, I2, j, I3]
36✔
256
                end
60✔
257
            end
21✔
258
        end
3✔
259
    end
3✔
260
    P
3✔
261
end
262

263
@noinline function _permutedims!(P::PermutedDimsArray, src, R1, R2, R3, ds, dp)
×
264
    ip, is = axes(src, dp), axes(src, ds)
×
265
    for jo in first(ip):8:last(ip), io in first(is):8:last(is)
×
266
        for I3 in R3, I2 in R2
×
267
            for j in jo:min(jo+7, last(ip))
×
268
                for i in io:min(io+7, last(is))
×
269
                    for I1 in R1
×
270
                        @inbounds P[I1, i, I2, j, I3] = src[I1, i, I2, j, I3]
×
271
                    end
×
272
                end
×
273
            end
×
274
        end
×
275
    end
×
276
    P
×
277
end
278

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

281
function Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::PermutedDimsArray, dims::Colon)
12✔
282
    Base._mapreduce_dim(f, op, init, parent(A), dims)
12✔
283
end
284
function Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(Base.mul_prod),typeof(*)}, init::Base._InitialValue, A::PermutedDimsArray{<:Union{Real,Complex}}, dims::Colon)
×
285
    Base._mapreduce_dim(f, op, init, parent(A), dims)
×
286
end
287

288
function Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray{T,N}, A::PermutedDimsArray{S,N,perm,iperm}) where {T,S,N,perm,iperm}
×
289
    C = PermutedDimsArray{T,N,iperm,perm,typeof(B)}(B) # make the inverse permutation for the output
×
290
    Base.mapreducedim!(f, op, C, parent(A))
×
291
    B
×
292
end
293
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}
×
294
    C = PermutedDimsArray{T,N,iperm,perm,typeof(B)}(B) # make the inverse permutation for the output
×
295
    Base.mapreducedim!(f, op, C, parent(A))
×
296
    B
×
297
end
298

299
function Base.showarg(io::IO, A::PermutedDimsArray{T,N,perm}, toplevel) where {T,N,perm}
2✔
300
    print(io, "PermutedDimsArray(")
2✔
301
    Base.showarg(io, parent(A), false)
2✔
302
    print(io, ", ", perm, ')')
2✔
303
    toplevel && print(io, " with eltype ", eltype(A))
2✔
304
    return nothing
2✔
305
end
306

307
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