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

JuliaLang / julia / #37728

26 Mar 2024 03:46AM UTC coverage: 80.612% (-0.8%) from 81.423%
#37728

push

local

web-flow
Update zlib to 1.3.1 (#53841)

Released January 22, 2024

69920 of 86737 relevant lines covered (80.61%)

14456248.65 hits per line

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

83.23
/stdlib/LinearAlgebra/src/adjtrans.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
### basic definitions (types, aliases, constructors, abstractarray interface, sundry similar)
4

5
# note that Adjoint and Transpose must be able to wrap not only vectors and matrices
6
# but also factorizations, rotations, and other linear algebra objects, including
7
# user-defined such objects. so do not restrict the wrapped type.
8
"""
9
    Adjoint
10

11
Lazy wrapper type for an adjoint view of the underlying linear algebra object,
12
usually an `AbstractVector`/`AbstractMatrix`.
13
Usually, the `Adjoint` constructor should not be called directly, use [`adjoint`](@ref)
14
instead. To materialize the view use [`copy`](@ref).
15

16
This type is intended for linear algebra usage - for general data manipulation see
17
[`permutedims`](@ref Base.permutedims).
18

19
# Examples
20
```jldoctest
21
julia> A = [3+2im 9+2im; 0 0]
22
2×2 Matrix{Complex{Int64}}:
23
 3+2im  9+2im
24
 0+0im  0+0im
25

26
julia> Adjoint(A)
27
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
28
 3-2im  0+0im
29
 9-2im  0+0im
30
```
31
"""
32
struct Adjoint{T,S} <: AbstractMatrix{T}
33
    parent::S
4,067,256✔
34
end
35
"""
36
    Transpose
37

38
Lazy wrapper type for a transpose view of the underlying linear algebra object,
39
usually an `AbstractVector`/`AbstractMatrix`.
40
Usually, the `Transpose` constructor should not be called directly, use [`transpose`](@ref)
41
instead. To materialize the view use [`copy`](@ref).
42

43
This type is intended for linear algebra usage - for general data manipulation see
44
[`permutedims`](@ref Base.permutedims).
45

46
# Examples
47
```jldoctest
48
julia> A = [2 3; 0 0]
49
2×2 Matrix{Int64}:
50
 2  3
51
 0  0
52

53
julia> Transpose(A)
54
2×2 transpose(::Matrix{Int64}) with eltype Int64:
55
 2  0
56
 3  0
57
```
58
"""
59
struct Transpose{T,S} <: AbstractMatrix{T}
60
    parent::S
53,072✔
61
end
62

63
# basic outer constructors
64
Adjoint(A) = Adjoint{Base.promote_op(adjoint,eltype(A)),typeof(A)}(A)
4,067,235✔
65
Transpose(A) = Transpose{Base.promote_op(transpose,eltype(A)),typeof(A)}(A)
53,058✔
66

67
"""
68
    inplace_adj_or_trans(::AbstractArray) -> adjoint!|transpose!|copyto!
69
    inplace_adj_or_trans(::Type{<:AbstractArray}) -> adjoint!|transpose!|copyto!
70

71
Return [`adjoint!`](@ref) from an `Adjoint` type or object and
72
[`transpose!`](@ref) from a `Transpose` type or object. Otherwise,
73
return [`copyto!`](@ref). Note that `Adjoint` and `Transpose` have
74
to be the outer-most wrapper object for a non-`identity` function to be
75
returned.
76
"""
77
inplace_adj_or_trans(::T) where {T <: AbstractArray} = inplace_adj_or_trans(T)
7,660✔
78
inplace_adj_or_trans(::Type{<:AbstractArray}) = copyto!
×
79
inplace_adj_or_trans(::Type{<:Adjoint}) = adjoint!
4,920✔
80
inplace_adj_or_trans(::Type{<:Transpose}) = transpose!
2,740✔
81

82
# unwraps Adjoint, Transpose, Symmetric, Hermitian
83
_unwrap(A::Adjoint)   = parent(A)
4,019,362✔
84
_unwrap(A::Transpose) = parent(A)
11,751✔
85

86
# unwraps Adjoint and Transpose only
87
_unwrap_at(A) = A
32,208✔
88
_unwrap_at(A::Adjoint)   = parent(A)
14,778✔
89
_unwrap_at(A::Transpose) = parent(A)
12,689✔
90

91
Base.dataids(A::Union{Adjoint, Transpose}) = Base.dataids(A.parent)
10,314✔
92
Base.unaliascopy(A::Union{Adjoint,Transpose}) = typeof(A)(Base.unaliascopy(A.parent))
3✔
93

94
# wrapping lowercase quasi-constructors
95
"""
96
    A'
97
    adjoint(A)
98

99
Lazy adjoint (conjugate transposition). Note that `adjoint` is applied recursively to
100
elements.
101

102
For number types, `adjoint` returns the complex conjugate, and therefore it is equivalent to
103
the identity function for real numbers.
104

105
This operation is intended for linear algebra usage - for general data manipulation see
106
[`permutedims`](@ref Base.permutedims).
107

108
# Examples
109
```jldoctest
110
julia> A = [3+2im 9+2im; 0  0]
111
2×2 Matrix{Complex{Int64}}:
112
 3+2im  9+2im
113
 0+0im  0+0im
114

115
julia> B = A' # equivalently adjoint(A)
116
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
117
 3-2im  0+0im
118
 9-2im  0+0im
119

120
julia> B isa Adjoint
121
true
122

123
julia> adjoint(B) === A # the adjoint of an adjoint unwraps the parent
124
true
125

126
julia> Adjoint(B) # however, the constructor always wraps its argument
127
2×2 adjoint(adjoint(::Matrix{Complex{Int64}})) with eltype Complex{Int64}:
128
 3+2im  9+2im
129
 0+0im  0+0im
130

131
julia> B[1,2] = 4 + 5im; # modifying B will modify A automatically
132

133
julia> A
134
2×2 Matrix{Complex{Int64}}:
135
 3+2im  9+2im
136
 4-5im  0+0im
137
```
138

139
For real matrices, the `adjoint` operation is equivalent to a `transpose`.
140

141
```jldoctest
142
julia> A = reshape([x for x in 1:4], 2, 2)
143
2×2 Matrix{Int64}:
144
 1  3
145
 2  4
146

147
julia> A'
148
2×2 adjoint(::Matrix{Int64}) with eltype Int64:
149
 1  2
150
 3  4
151

152
julia> adjoint(A) == transpose(A)
153
true
154
```
155

156
The adjoint of an `AbstractVector` is a row-vector:
157
```jldoctest
158
julia> x = [3, 4im]
159
2-element Vector{Complex{Int64}}:
160
 3 + 0im
161
 0 + 4im
162

163
julia> x'
164
1×2 adjoint(::Vector{Complex{Int64}}) with eltype Complex{Int64}:
165
 3+0im  0-4im
166

167
julia> x'x # compute the dot product, equivalently x' * x
168
25 + 0im
169
```
170

171
For a matrix of matrices, the individual blocks are recursively operated on:
172
```jldoctest
173
julia> A = reshape([x + im*x for x in 1:4], 2, 2)
174
2×2 Matrix{Complex{Int64}}:
175
 1+1im  3+3im
176
 2+2im  4+4im
177

178
julia> C = reshape([A, 2A, 3A, 4A], 2, 2)
179
2×2 Matrix{Matrix{Complex{Int64}}}:
180
 [1+1im 3+3im; 2+2im 4+4im]  [3+3im 9+9im; 6+6im 12+12im]
181
 [2+2im 6+6im; 4+4im 8+8im]  [4+4im 12+12im; 8+8im 16+16im]
182

183
julia> C'
184
2×2 adjoint(::Matrix{Matrix{Complex{Int64}}}) with eltype Adjoint{Complex{Int64}, Matrix{Complex{Int64}}}:
185
 [1-1im 2-2im; 3-3im 4-4im]    [2-2im 4-4im; 6-6im 8-8im]
186
 [3-3im 6-6im; 9-9im 12-12im]  [4-4im 8-8im; 12-12im 16-16im]
187
```
188
"""
189
adjoint(A::AbstractVecOrMat) = Adjoint(A)
4,061,538✔
190

191
"""
192
    transpose(A)
193

194
Lazy transpose. Mutating the returned object should appropriately mutate `A`. Often,
195
but not always, yields `Transpose(A)`, where `Transpose` is a lazy transpose wrapper. Note
196
that this operation is recursive.
197

198
This operation is intended for linear algebra usage - for general data manipulation see
199
[`permutedims`](@ref Base.permutedims), which is non-recursive.
200

201
# Examples
202
```jldoctest
203
julia> A = [3 2; 0 0]
204
2×2 Matrix{Int64}:
205
 3  2
206
 0  0
207

208
julia> B = transpose(A)
209
2×2 transpose(::Matrix{Int64}) with eltype Int64:
210
 3  0
211
 2  0
212

213
julia> B isa Transpose
214
true
215

216
julia> transpose(B) === A # the transpose of a transpose unwraps the parent
217
true
218

219
julia> Transpose(B) # however, the constructor always wraps its argument
220
2×2 transpose(transpose(::Matrix{Int64})) with eltype Int64:
221
 3  2
222
 0  0
223

224
julia> B[1,2] = 4; # modifying B will modify A automatically
225

226
julia> A
227
2×2 Matrix{Int64}:
228
 3  2
229
 4  0
230
```
231

232
For complex matrices, the `adjoint` operation is equivalent to a conjugate-transpose.
233
```jldoctest
234
julia> A = reshape([Complex(x, x) for x in 1:4], 2, 2)
235
2×2 Matrix{Complex{Int64}}:
236
 1+1im  3+3im
237
 2+2im  4+4im
238

239
julia> adjoint(A) == conj(transpose(A))
240
true
241
```
242

243
The `transpose` of an `AbstractVector` is a row-vector:
244
```jldoctest
245
julia> v = [1,2,3]
246
3-element Vector{Int64}:
247
 1
248
 2
249
 3
250

251
julia> transpose(v) # returns a row-vector
252
1×3 transpose(::Vector{Int64}) with eltype Int64:
253
 1  2  3
254

255
julia> transpose(v) * v # compute the dot product
256
14
257
```
258

259
For a matrix of matrices, the individual blocks are recursively operated on:
260
```jldoctest
261
julia> C = [1 3; 2 4]
262
2×2 Matrix{Int64}:
263
 1  3
264
 2  4
265

266
julia> D = reshape([C, 2C, 3C, 4C], 2, 2) # construct a block matrix
267
2×2 Matrix{Matrix{Int64}}:
268
 [1 3; 2 4]  [3 9; 6 12]
269
 [2 6; 4 8]  [4 12; 8 16]
270

271
julia> transpose(D) # blocks are recursively transposed
272
2×2 transpose(::Matrix{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}:
273
 [1 2; 3 4]   [2 4; 6 8]
274
 [3 6; 9 12]  [4 8; 12 16]
275
```
276
"""
277
transpose(A::AbstractVecOrMat) = Transpose(A)
49,325✔
278

279
# unwrapping lowercase quasi-constructors
280
adjoint(A::Adjoint) = A.parent
2,541✔
281
transpose(A::Transpose) = A.parent
1,879✔
282
adjoint(A::Transpose{<:Real}) = A.parent
476✔
283
transpose(A::Adjoint{<:Real}) = A.parent
216✔
284
adjoint(A::Transpose{<:Any,<:Adjoint}) = transpose(A.parent.parent)
×
285
transpose(A::Adjoint{<:Any,<:Transpose}) = adjoint(A.parent.parent)
27✔
286
# disambiguation
287
adjoint(A::Transpose{<:Real,<:Adjoint}) = transpose(A.parent.parent)
×
288
transpose(A::Adjoint{<:Real,<:Transpose}) = A.parent
×
289

290
# printing
291
function Base.showarg(io::IO, v::Adjoint, toplevel)
3✔
292
    print(io, "adjoint(")
3✔
293
    Base.showarg(io, parent(v), false)
3✔
294
    print(io, ')')
3✔
295
    toplevel && print(io, " with eltype ", eltype(v))
3✔
296
    return nothing
3✔
297
end
298
function Base.showarg(io::IO, v::Transpose, toplevel)
2✔
299
    print(io, "transpose(")
2✔
300
    Base.showarg(io, parent(v), false)
2✔
301
    print(io, ')')
2✔
302
    toplevel && print(io, " with eltype ", eltype(v))
2✔
303
    return nothing
2✔
304
end
305

306
# some aliases for internal convenience use
307
const AdjOrTrans{T,S} = Union{Adjoint{T,S},Transpose{T,S}} where {T,S}
308
const AdjointAbsVec{T} = Adjoint{T,<:AbstractVector}
309
const AdjointAbsMat{T} = Adjoint{T,<:AbstractMatrix}
310
const TransposeAbsVec{T} = Transpose{T,<:AbstractVector}
311
const TransposeAbsMat{T} = Transpose{T,<:AbstractMatrix}
312
const AdjOrTransAbsVec{T} = AdjOrTrans{T,<:AbstractVector}
313
const AdjOrTransAbsMat{T} = AdjOrTrans{T,<:AbstractMatrix}
314

315
# for internal use below
316
wrapperop(_) = identity
70,340✔
317
wrapperop(::Adjoint) = adjoint
×
318
wrapperop(::Transpose) = transpose
×
319

320
# the following fallbacks can be removed if Adjoint/Transpose are restricted to AbstractVecOrMat
321
size(A::AdjOrTrans) = reverse(size(A.parent))
×
322
axes(A::AdjOrTrans) = reverse(axes(A.parent))
×
323
# AbstractArray interface, basic definitions
324
length(A::AdjOrTrans) = length(A.parent)
35,105✔
325
size(v::AdjOrTransAbsVec) = (1, length(v.parent))
2,540✔
326
size(A::AdjOrTransAbsMat) = reverse(size(A.parent))
126,903✔
327
axes(v::AdjOrTransAbsVec) = (axes(v.parent,2), axes(v.parent)...)
327,079✔
328
axes(A::AdjOrTransAbsMat) = reverse(axes(A.parent))
416,411✔
329
IndexStyle(::Type{<:AdjOrTransAbsVec}) = IndexLinear()
311,634✔
330
IndexStyle(::Type{<:AdjOrTransAbsMat}) = IndexCartesian()
315,678✔
331
@propagate_inbounds Base.isassigned(v::AdjOrTransAbsVec, i::Int) = isassigned(v.parent, i-1+first(axes(v.parent)[1]))
4,356✔
332
@propagate_inbounds Base.isassigned(v::AdjOrTransAbsMat, i::Int, j::Int) = isassigned(v.parent, j, i)
60,607✔
333
@propagate_inbounds getindex(v::AdjOrTransAbsVec{T}, i::Int) where {T} = wrapperop(v)(v.parent[i-1+first(axes(v.parent)[1])])::T
160,929✔
334
@propagate_inbounds getindex(A::AdjOrTransAbsMat{T}, i::Int, j::Int) where {T} = wrapperop(A)(A.parent[j, i])::T
13,796,464✔
335
@propagate_inbounds setindex!(v::AdjOrTransAbsVec, x, i::Int) = (setindex!(v.parent, wrapperop(v)(x), i-1+first(axes(v.parent)[1])); v)
12,556✔
336
@propagate_inbounds setindex!(A::AdjOrTransAbsMat, x, i::Int, j::Int) = (setindex!(A.parent, wrapperop(A)(x), j, i); A)
27,188✔
337
# AbstractArray interface, additional definitions to retain wrapper over vectors where appropriate
338
@propagate_inbounds getindex(v::AdjOrTransAbsVec, ::Colon, is::AbstractArray{Int}) = wrapperop(v)(v.parent[is])
8✔
339
@propagate_inbounds getindex(v::AdjOrTransAbsVec, ::Colon, ::Colon) = wrapperop(v)(v.parent[:])
8✔
340

341
# conversion of underlying storage
342
convert(::Type{Adjoint{T,S}}, A::Adjoint) where {T,S} = Adjoint{T,S}(convert(S, A.parent))::Adjoint{T,S}
8✔
343
convert(::Type{Transpose{T,S}}, A::Transpose) where {T,S} = Transpose{T,S}(convert(S, A.parent))::Transpose{T,S}
2✔
344

345
# Strides and pointer for transposed strided arrays — but only if the elements are actually stored in memory
346
Base.strides(A::Adjoint{<:Real, <:AbstractVector}) = (stride(A.parent, 2), stride(A.parent, 1))
4✔
347
Base.strides(A::Transpose{<:Any, <:AbstractVector}) = (stride(A.parent, 2), stride(A.parent, 1))
6✔
348
# For matrices it's slightly faster to use reverse and avoid calling stride twice
349
Base.strides(A::Adjoint{<:Real, <:AbstractMatrix}) = reverse(strides(A.parent))
2,583✔
350
Base.strides(A::Transpose{<:Any, <:AbstractMatrix}) = reverse(strides(A.parent))
2,585✔
351

352
Base.cconvert(::Type{Ptr{T}}, A::Adjoint{<:Real, <:AbstractVecOrMat}) where {T} = Base.cconvert(Ptr{T}, A.parent)
2,055✔
353
Base.cconvert(::Type{Ptr{T}}, A::Transpose{<:Any, <:AbstractVecOrMat}) where {T} = Base.cconvert(Ptr{T}, A.parent)
2,059✔
354

355
Base.elsize(::Type{<:Adjoint{<:Real, P}}) where {P<:AbstractVecOrMat} = Base.elsize(P)
2,580✔
356
Base.elsize(::Type{<:Transpose{<:Any, P}}) where {P<:AbstractVecOrMat} = Base.elsize(P)
2,580✔
357

358
# for vectors, the semantics of the wrapped and unwrapped types differ
359
# so attempt to maintain both the parent and wrapper type insofar as possible
360
similar(A::AdjOrTransAbsVec) = wrapperop(A)(similar(A.parent))
172✔
361
similar(A::AdjOrTransAbsVec, ::Type{T}) where {T} = wrapperop(A)(similar(A.parent, Base.promote_op(wrapperop(A), T)))
18✔
362
# for matrices, the semantics of the wrapped and unwrapped types are generally the same
363
# and as you are allocating with similar anyway, you might as well get something unwrapped
364
similar(A::AdjOrTrans) = similar(A.parent, eltype(A), axes(A))
6✔
365
similar(A::AdjOrTrans, ::Type{T}) where {T} = similar(A.parent, T, axes(A))
4,664✔
366
similar(A::AdjOrTrans, ::Type{T}, dims::Dims{N}) where {T,N} = similar(A.parent, T, dims)
48,368✔
367

368
# AbstractMatrix{T} constructor for adjtrans vector: preserve wrapped type
369
AbstractMatrix{T}(A::AdjOrTransAbsVec) where {T} = wrapperop(A)(AbstractVector{T}(A.parent))
10✔
370

371
# sundry basic definitions
372
parent(A::AdjOrTrans) = A.parent
4,076,861✔
373
vec(v::TransposeAbsVec{<:Number}) = parent(v)
13✔
374
vec(v::AdjointAbsVec{<:Real}) = parent(v)
292✔
375

376
### concatenation
377
# preserve Adjoint/Transpose wrapper around vectors
378
# to retain the associated semantics post-concatenation
379
hcat(avs::Union{Number,AdjointAbsVec}...) = _adjoint_hcat(avs...)
1✔
380
hcat(tvs::Union{Number,TransposeAbsVec}...) = _transpose_hcat(tvs...)
1✔
381
_adjoint_hcat(avs::Union{Number,AdjointAbsVec}...) = adjoint(vcat(map(adjoint, avs)...))
5✔
382
_transpose_hcat(tvs::Union{Number,TransposeAbsVec}...) = transpose(vcat(map(transpose, tvs)...))
3✔
383
typed_hcat(::Type{T}, avs::Union{Number,AdjointAbsVec}...) where {T} = adjoint(typed_vcat(T, map(adjoint, avs)...))
×
384
typed_hcat(::Type{T}, tvs::Union{Number,TransposeAbsVec}...) where {T} = transpose(typed_vcat(T, map(transpose, tvs)...))
×
385
# otherwise-redundant definitions necessary to prevent hitting the concat methods in LinearAlgebra/special.jl
386
hcat(avs::Adjoint{<:Any,<:Vector}...) = _adjoint_hcat(avs...)
1✔
387
hcat(tvs::Transpose{<:Any,<:Vector}...) = _transpose_hcat(tvs...)
1✔
388
hcat(avs::Adjoint{T,Vector{T}}...) where {T} = _adjoint_hcat(avs...)
3✔
389
hcat(tvs::Transpose{T,Vector{T}}...) where {T} = _transpose_hcat(tvs...)
1✔
390
# TODO unify and allow mixed combinations
391

392

393
### higher order functions
394
# preserve Adjoint/Transpose wrapper around vectors
395
# to retain the associated semantics post-map/broadcast
396
#
397
# note that the caller's operation f operates in the domain of the wrapped vectors' entries.
398
# hence the adjoint->f->adjoint shenanigans applied to the parent vectors' entries.
399
function map(f, av::AdjointAbsVec, avs::AdjointAbsVec...)
4✔
400
    s = (av, avs...)
5✔
401
    adjoint(map((xs...) -> adjoint(f(adjoint.(xs)...)), parent.(s)...))
18✔
402
end
403
function map(f, tv::TransposeAbsVec, tvs::TransposeAbsVec...)
4✔
404
    s = (tv, tvs...)
5✔
405
    transpose(map((xs...) -> transpose(f(transpose.(xs)...)), parent.(s)...))
18✔
406
end
407
quasiparentt(x) = parent(x); quasiparentt(x::Number) = x # to handle numbers in the defs below
895✔
408
quasiparenta(x) = parent(x); quasiparenta(x::Number) = conj(x) # to handle numbers in the defs below
1,019✔
409
quasiparentc(x) = parent(parent(x)); quasiparentc(x::Number) = conj(x) # to handle numbers in the defs below
6✔
410
broadcast(f, avs::Union{Number,AdjointAbsVec}...) = adjoint(broadcast((xs...) -> adjoint(f(adjoint.(xs)...)), quasiparenta.(avs)...))
32✔
411
broadcast(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...) -> transpose(f(transpose.(xs)...)), quasiparentt.(tvs)...))
28✔
412
# Hack to preserve behavior after #32122; this needs to be done with a broadcast style instead to support dotted fusion
413
Broadcast.broadcast_preserving_zero_d(f, avs::Union{Number,AdjointAbsVec}...) = adjoint(broadcast((xs...) -> adjoint(f(adjoint.(xs)...)), quasiparenta.(avs)...))
5,001✔
414
Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...) -> transpose(f(transpose.(xs)...)), quasiparentt.(tvs)...))
4,123✔
415
Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,Transpose{<:Any,<:AdjointAbsVec}}...) =
×
416
    transpose(adjoint(broadcast((xs...) -> adjoint(transpose(f(conj.(xs)...))), quasiparentc.(tvs)...)))
×
417
Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,Adjoint{<:Any,<:TransposeAbsVec}}...) =
3✔
418
    adjoint(transpose(broadcast((xs...) -> transpose(adjoint(f(conj.(xs)...))), quasiparentc.(tvs)...)))
9✔
419
# TODO unify and allow mixed combinations with a broadcast style
420

421

422
### reductions
423
# faster to sum the Array than to work through the wrapper (but only in commutative reduction ops as in Base/permuteddimsarray.jl)
424
Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::Transpose, dims::Colon) =
14✔
425
    Base._mapreduce_dim(f∘transpose, op, init, parent(A), dims)
426
Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::Adjoint, dims::Colon) =
20✔
427
    Base._mapreduce_dim(f∘adjoint, op, init, parent(A), dims)
428
# in prod, use fast path only in the commutative case to avoid surprises
429
Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, init::Base._InitialValue, A::Transpose{<:Union{Real,Complex}}, dims::Colon) =
1✔
430
    Base._mapreduce_dim(f∘transpose, op, init, parent(A), dims)
431
Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, init::Base._InitialValue, A::Adjoint{<:Union{Real,Complex}}, dims::Colon) =
1✔
432
    Base._mapreduce_dim(f∘adjoint, op, init, parent(A), dims)
433
# count allows for optimization only if the parent array has Bool eltype
434
Base._count(::typeof(identity), A::Transpose{Bool}, ::Colon, init) = Base._count(identity, parent(A), :, init)
×
435
Base._count(::typeof(identity), A::Adjoint{Bool}, ::Colon, init) = Base._count(identity, parent(A), :, init)
×
436
Base._any(f, A::Transpose, ::Colon) = Base._any(f∘transpose, parent(A), :)
×
437
Base._any(f, A::Adjoint, ::Colon) = Base._any(f∘adjoint, parent(A), :)
×
438
Base._all(f, A::Transpose, ::Colon) = Base._all(f∘transpose, parent(A), :)
×
439
Base._all(f, A::Adjoint, ::Colon) = Base._all(f∘adjoint, parent(A), :)
×
440
# sum(A'; dims)
441
Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray, A::TransposeAbsMat) =
41✔
442
    (Base.mapreducedim!(f∘transpose, op, switch_dim12(B), parent(A)); B)
41✔
443
Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray, A::AdjointAbsMat) =
41✔
444
    (Base.mapreducedim!(f∘adjoint, op, switch_dim12(B), parent(A)); B)
41✔
445
Base.mapreducedim!(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, B::AbstractArray, A::TransposeAbsMat{<:Union{Real,Complex}}) =
6✔
446
    (Base.mapreducedim!(f∘transpose, op, switch_dim12(B), parent(A)); B)
6✔
447
Base.mapreducedim!(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, B::AbstractArray, A::AdjointAbsMat{<:Union{Real,Complex}}) =
6✔
448
    (Base.mapreducedim!(f∘adjoint, op, switch_dim12(B), parent(A)); B)
6✔
449

450
switch_dim12(B::AbstractVector) = permutedims(B)
×
451
switch_dim12(B::AbstractVector{<:Number}) = transpose(B) # avoid allocs due to permutedims
×
452
switch_dim12(B::AbstractArray{<:Any,0}) = B
×
453
switch_dim12(B::AbstractArray) = PermutedDimsArray(B, (2, 1, ntuple(Base.Fix1(+,2), ndims(B) - 2)...))
94✔
454

455
### linear algebra
456

457
(-)(A::Adjoint)   = Adjoint(  -A.parent)
25✔
458
(-)(A::Transpose) = Transpose(-A.parent)
4✔
459

460
tr(A::Adjoint) = adjoint(tr(parent(A)))
2✔
461
tr(A::Transpose) = transpose(tr(parent(A)))
2✔
462

463
## multiplication *
464

465
function _dot_nonrecursive(u, v)
17✔
466
    lu = length(u)
73✔
467
    if lu != length(v)
73✔
468
        throw(DimensionMismatch("first array has length $(lu) which does not match the length of the second, $(length(v))."))
×
469
    end
470
    if lu == 0
73✔
471
        zero(eltype(u)) * zero(eltype(v))
×
472
    else
473
        sum(uu*vv for (uu, vv) in zip(u, v))
73✔
474
    end
475
end
476

477
# Adjoint/Transpose-vector * vector
478
*(u::AdjointAbsVec{<:Number}, v::AbstractVector{<:Number}) = dot(u.parent, v)
278✔
479
*(u::TransposeAbsVec{T}, v::AbstractVector{T}) where {T<:Real} = dot(u.parent, v)
62✔
480
*(u::AdjOrTransAbsVec, v::AbstractVector) = _dot_nonrecursive(u, v)
28✔
481

482

483
# vector * Adjoint/Transpose-vector
484
*(u::AbstractVector, v::AdjOrTransAbsVec) = broadcast(*, u, v)
267✔
485

486
# AdjOrTransAbsVec{<:Any,<:AdjOrTransAbsVec} is a lazy conj vectors
487
# We need to expand the combinations to avoid ambiguities
488
(*)(u::TransposeAbsVec, v::AdjointAbsVec{<:Any,<:TransposeAbsVec}) = _dot_nonrecursive(u, v)
90✔
489
(*)(u::AdjointAbsVec,   v::AdjointAbsVec{<:Any,<:TransposeAbsVec}) = _dot_nonrecursive(u, v)
×
490
(*)(u::TransposeAbsVec, v::TransposeAbsVec{<:Any,<:AdjointAbsVec}) = _dot_nonrecursive(u, v)
×
491
(*)(u::AdjointAbsVec,   v::TransposeAbsVec{<:Any,<:AdjointAbsVec}) = _dot_nonrecursive(u, v)
×
492

493
## pseudoinversion
494
pinv(v::AdjointAbsVec, tol::Real = 0) = pinv(v.parent, tol).parent
160✔
495
pinv(v::TransposeAbsVec, tol::Real = 0) = pinv(conj(v.parent)).parent
80✔
496

497

498
## left-division \
499
\(u::AdjOrTransAbsVec, v::AdjOrTransAbsVec) = pinv(u) * v
44✔
500

501

502
## right-division /
503
/(u::AdjointAbsVec, A::AbstractMatrix) = adjoint(adjoint(A) \ u.parent)
32✔
504
/(u::TransposeAbsVec, A::AbstractMatrix) = transpose(transpose(A) \ u.parent)
70✔
505
/(u::AdjointAbsVec, A::TransposeAbsMat) = adjoint(conj(A.parent) \ u.parent) # technically should be adjoint(copy(adjoint(copy(A))) \ u.parent)
8✔
506
/(u::TransposeAbsVec, A::AdjointAbsMat) = transpose(conj(A.parent) \ u.parent) # technically should be transpose(copy(transpose(copy(A))) \ u.parent)
5✔
507

508
## complex conjugate
509
conj(A::Transpose) = adjoint(A.parent)
33✔
510
conj(A::Adjoint) = transpose(A.parent)
32✔
511

512
## structured matrix methods ##
513
function Base.replace_in_print_matrix(A::AdjOrTrans,i::Integer,j::Integer,s::AbstractString)
516✔
514
    Base.replace_in_print_matrix(parent(A), j, i, s)
644✔
515
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

© 2025 Coveralls, Inc