Skip to content

Commit 4188b2c

Browse files
ZeppLudkarrasch
andauthored
Add some (matrix) functions for UniformScaling (#28872)
Co-authored-by: Daniel Karrasch <[email protected]>
1 parent 6ade90a commit 4188b2c

File tree

2 files changed

+122
-4
lines changed

2 files changed

+122
-4
lines changed

stdlib/LinearAlgebra/src/uniformscaling.jl

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ end
8888
copy(J::UniformScaling) = UniformScaling(J.λ)
8989

9090
conj(J::UniformScaling) = UniformScaling(conj(J.λ))
91+
real(J::UniformScaling) = UniformScaling(real(J.λ))
92+
imag(J::UniformScaling) = UniformScaling(imag(J.λ))
9193

9294
transpose(J::UniformScaling) = J
9395
adjoint(J::UniformScaling) = UniformScaling(conj(J.λ))
@@ -106,11 +108,15 @@ issymmetric(::UniformScaling) = true
106108
ishermitian(J::UniformScaling) = isreal(J.λ)
107109
isposdef(J::UniformScaling) = isposdef(J.λ)
108110

111+
isposdef(J::UniformScaling) = isposdef(J.λ)
112+
109113
(+)(J::UniformScaling, x::Number) = J.λ + x
110114
(+)(x::Number, J::UniformScaling) = x + J.λ
111115
(-)(J::UniformScaling, x::Number) = J.λ - x
112116
(-)(x::Number, J::UniformScaling) = x - J.λ
113117

118+
(^)(J::UniformScaling, x::Number) = UniformScaling(J.λ ^ x)
119+
114120
(+)(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ+J2.λ)
115121
(+)(B::BitArray{2}, J::UniformScaling) = Array(B) + J
116122
(+)(J::UniformScaling, B::BitArray{2}) = J + Array(B)
@@ -122,6 +128,21 @@ isposdef(J::UniformScaling) = isposdef(J.λ)
122128
(-)(J::UniformScaling, B::BitArray{2}) = J - Array(B)
123129
(-)(A::AbstractMatrix, J::UniformScaling) = A + (-J)
124130

131+
# matrix functions
132+
for f in ( :exp, :log,
133+
:expm1, :log1p,
134+
:sqrt, :cbrt,
135+
:sin, :cos, :tan,
136+
:asin, :acos, :atan,
137+
:csc, :sec, :cot,
138+
:acsc, :asec, :acot,
139+
:sinh, :cosh, :tanh,
140+
:asinh, :acosh, :atanh,
141+
:csch, :sech, :coth,
142+
:acsch, :asech, :acoth )
143+
@eval Base.$f(J::UniformScaling) = UniformScaling($f(J.λ))
144+
end
145+
125146
# Unit{Lower/Upper}Triangular matrices become {Lower/Upper}Triangular under
126147
# addition with a UniformScaling
127148
for (t1, t2) in ((:UnitUpperTriangular, :UpperTriangular),
@@ -181,6 +202,10 @@ end
181202
inv(J::UniformScaling) = UniformScaling(inv(J.λ))
182203
opnorm(J::UniformScaling, p::Real=2) = opnorm(J.λ, p)
183204

205+
pinv(J::UniformScaling) = ifelse(iszero(J.λ),
206+
UniformScaling(zero(inv(J.λ))), # type stability
207+
UniformScaling(inv(J.λ)))
208+
184209
function det(J::UniformScaling{T}) where T
185210
if isone(J.λ)
186211
one(T)
@@ -191,23 +216,31 @@ function det(J::UniformScaling{T}) where T
191216
end
192217
end
193218

219+
function tr(J::UniformScaling{T}) where T
220+
if iszero(J.λ)
221+
zero(T)
222+
else
223+
throw(ArgumentError("Trace of UniformScaling is only well-defined when λ = 0"))
224+
end
225+
end
226+
194227
*(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ*J2.λ)
195228
*(B::BitArray{2}, J::UniformScaling) = *(Array(B), J::UniformScaling)
196229
*(J::UniformScaling, B::BitArray{2}) = *(J::UniformScaling, Array(B))
197230
*(A::AbstractMatrix, J::UniformScaling) = A*J.λ
231+
*(v::AbstractVector, J::UniformScaling) = reshape(v, length(v), 1) * J
198232
*(J::UniformScaling, A::AbstractVecOrMat) = J.λ*A
199233
*(x::Number, J::UniformScaling) = UniformScaling(x*J.λ)
200234
*(J::UniformScaling, x::Number) = UniformScaling(J.λ*x)
201235

202236
/(J1::UniformScaling, J2::UniformScaling) = J2.λ == 0 ? throw(SingularException(1)) : UniformScaling(J1.λ/J2.λ)
203237
/(J::UniformScaling, A::AbstractMatrix) = lmul!(J.λ, inv(A))
204238
/(A::AbstractMatrix, J::UniformScaling) = J.λ == 0 ? throw(SingularException(1)) : A/J.λ
239+
/(v::AbstractVector, J::UniformScaling) = reshape(v, length(v), 1) / J
205240

206241
/(J::UniformScaling, x::Number) = UniformScaling(J.λ/x)
207242

208243
\(J1::UniformScaling, J2::UniformScaling) = J1.λ == 0 ? throw(SingularException(1)) : UniformScaling(J1.λ\J2.λ)
209-
\(A::Union{Bidiagonal{T},AbstractTriangular{T}}, J::UniformScaling) where {T<:Number} =
210-
rmul!(inv(A), J.λ)
211244
\(J::UniformScaling, A::AbstractVecOrMat) = J.λ == 0 ? throw(SingularException(1)) : J.λ\A
212245
\(A::AbstractMatrix, J::UniformScaling) = rmul!(inv(A), J.λ)
213246
\(F::Factorization, J::UniformScaling) = F \ J(size(F,1))
@@ -229,6 +262,10 @@ Broadcast.broadcasted(::typeof(*), J::UniformScaling,x::Number) = UniformScaling
229262

230263
Broadcast.broadcasted(::typeof(/), J::UniformScaling,x::Number) = UniformScaling(J.λ/x)
231264

265+
Broadcast.broadcasted(::typeof(\), x::Number,J::UniformScaling) = UniformScaling(x\J.λ)
266+
267+
Broadcast.broadcasted(::typeof(^), J::UniformScaling,x::Number) = UniformScaling(J.λ^x)
268+
232269
(^)(J::UniformScaling, x::Number) = UniformScaling((J.λ)^x)
233270
Base.literal_pow(::typeof(^), J::UniformScaling, x::Val) = UniformScaling(Base.literal_pow(^, J.λ, x))
234271

stdlib/LinearAlgebra/test/uniformscaling.jl

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,53 @@ Random.seed!(123)
2727
@test opnorm(UniformScaling(1+im)) sqrt(2)
2828
end
2929

30+
@testset "sqrt, exp, log, and trigonometric functions" begin
31+
# convert to a dense matrix with random size
32+
M(J) = (N = rand(1:10); Matrix(J, N, N))
33+
34+
# on complex plane
35+
J = UniformScaling(randn(ComplexF64))
36+
for f in ( exp, log,
37+
sqrt,
38+
sin, cos, tan,
39+
asin, acos, atan,
40+
csc, sec, cot,
41+
acsc, asec, acot,
42+
sinh, cosh, tanh,
43+
asinh, acosh, atanh,
44+
csch, sech, coth,
45+
acsch, asech, acoth )
46+
@test f(J) f(M(J))
47+
end
48+
49+
# on real axis
50+
for (λ, fs) in (
51+
# functions defined for x ∈ ℝ
52+
(()->randn(), (exp,
53+
sin, cos, tan,
54+
csc, sec, cot,
55+
atan, acot,
56+
sinh, cosh, tanh,
57+
csch, sech, coth,
58+
asinh, acsch)),
59+
# functions defined for x ≥ 0
60+
(()->abs(randn()), (log, sqrt)),
61+
# functions defined for -1 ≤ x ≤ 1
62+
(()->2rand()-1, (asin, acos, atanh)),
63+
# functions defined for x ≤ -1 or x ≥ 1
64+
(()->1/(2rand()-1), (acsc, asec, acoth)),
65+
# functions defined for 0 ≤ x ≤ 1
66+
(()->rand(), (asech,)),
67+
# functions defined for x ≥ 1
68+
(()->1/rand(), (acosh,))
69+
)
70+
for f in fs
71+
J = UniformScaling(λ())
72+
@test f(J) f(M(J))
73+
end
74+
end
75+
end
76+
3077
@testset "conjugation of UniformScaling" begin
3178
@test conj(UniformScaling(1))::UniformScaling{Int} == UniformScaling(1)
3279
@test conj(UniformScaling(1.0))::UniformScaling{Float64} == UniformScaling(1.0)
@@ -42,6 +89,10 @@ end
4289
@test issymmetric(UniformScaling(complex(1.0,1.0)))
4390
@test ishermitian(I)
4491
@test !ishermitian(UniformScaling(complex(1.0,1.0)))
92+
@test isposdef(UniformScaling(rand()))
93+
@test !isposdef(UniformScaling(-rand()))
94+
@test !isposdef(UniformScaling(randn(ComplexF64)))
95+
@test !isposdef(UniformScaling(NaN))
4596
@test isposdef(I)
4697
@test !isposdef(-I)
4798
@test isposdef(UniformScaling(complex(1.0, 0.0)))
@@ -64,8 +115,10 @@ end
64115
@test I - α == 1 - α
65116
@test α .* UniformScaling(1.0) == UniformScaling(1.0) .* α
66117
@test UniformScaling(α)./α == UniformScaling(1.0)
118+
@test α.\UniformScaling(α) == UniformScaling(1.0)
67119
@test α * UniformScaling(1.0) == UniformScaling(1.0) * α
68120
@test UniformScaling(α)/α == UniformScaling(1.0)
121+
@test (2I)^α == (2I).^α == (2^α)I
69122

70123
β = rand()
71124
@test*I)^2 == UniformScaling^2)
@@ -77,7 +130,11 @@ end
77130
@test* I) .^ β == UniformScaling^β)
78131
end
79132

80-
@testset "det and logdet" begin
133+
@testset "tr, det and logdet" begin
134+
for T in (Int, Float64, ComplexF64, Bool)
135+
@test tr(UniformScaling(zero(T))) === zero(T)
136+
end
137+
@test_throws ArgumentError tr(UniformScaling(1))
81138
@test det(I) === true
82139
@test det(1.0I) === 1.0
83140
@test det(0I) === 0
@@ -95,24 +152,48 @@ end
95152
let
96153
λ = complex(randn(),randn())
97154
J = UniformScaling(λ)
98-
@testset "transpose, conj, inv" begin
155+
@testset "transpose, conj, inv, pinv, cond" begin
99156
@test ndims(J) == 2
100157
@test transpose(J) == J
101158
@test J * [1 0; 0 1] == conj(*(adjoint(J), [1 0; 0 1])) # ctranpose (and A(c)_mul_B)
102159
@test I + I === UniformScaling(2) # +
103160
@test inv(I) == I
104161
@test inv(J) == UniformScaling(inv(λ))
162+
@test pinv(J) == UniformScaling(inv(λ))
163+
@test @inferred(pinv(0.0I)) == 0.0I
164+
@test @inferred(pinv(0I)) == 0.0I
165+
@test @inferred(pinv(false*I)) == 0.0I
166+
@test @inferred(pinv(0im*I)) == 0im*I
105167
@test cond(I) == 1
106168
@test cond(J) == zero(λ) ? one(real(λ)) : oftype(real(λ), Inf))
107169
end
108170

171+
@testset "real, imag, reim" begin
172+
@test real(J) == UniformScaling(real(λ))
173+
@test imag(J) == UniformScaling(imag(λ))
174+
@test reim(J) == (UniformScaling(real(λ)), UniformScaling(imag(λ)))
175+
end
176+
109177
@testset "copyto!" begin
110178
A = Matrix{Int}(undef, (3,3))
111179
@test copyto!(A, I) == one(A)
112180
B = Matrix{ComplexF64}(undef, (1,2))
113181
@test copyto!(B, J) ==zero(λ)]
114182
end
115183

184+
@testset "binary ops with vectors" begin
185+
v = complex.(randn(3), randn(3))
186+
# As shown in #20423@GitHub, vector acts like x1 matrix when participating in linear algebra
187+
@test v * J v * λ
188+
@test v' * J v' * λ
189+
@test J * v λ * v
190+
@test J * v' λ * v'
191+
@test v / J v / λ
192+
@test v' / J v' / λ
193+
@test J \ v λ \ v
194+
@test J \ v' λ \ v'
195+
end
196+
116197
@testset "binary ops with matrices" begin
117198
B = bitrand(2, 2)
118199
@test B + I == B + Matrix(I, size(B))

0 commit comments

Comments
 (0)