Skip to content

Commit 6378d7d

Browse files
afniedermayerandreasnoack
authored andcommittedAug 24, 2017
Make behavior of ^(::Matrix{<:Integer},::Integer) consistent with behavior of ^(::Integer, ::Integer) (#23368)
* Make `^(::Matrix{S},::T) where {S<:Integer, T<:Integer}` type stable. Also enure that the same promotion rules as `^(::S, ::T)` * Make `^(::Matrix{S},::T) where {S<:Integer, T<:Integer}` type stable. Also enure that the same promotion rules as `^(::S, ::T)` * fix typo * fixed typo * Adjust tests for `^(::Matrix{Int64},Int64)` for symmetric/hermitian matrices * avoid code duplication by using the same code for `^(::Matrix{<:Integer},::Integer)` as for `^(::Integer,::Integer)` * added specialized domain error message for `^(::Matrix{<:Integer},::Integer)`, added `@inferred` and comments to test * added specialized domain error message for `^(::Matrix{<:Integer},::Integer)`, added `@inferred` and comments to test * reverted change in faq * different throw_domerr_powbysq for Any and Integer, simplified conversion * Adjusted "Breaking changes" section of news * split news item into two bullets * remove unnecessary Base. qualification * remove more unnecessary Base. qualifications
1 parent edd8278 commit 6378d7d

File tree

5 files changed

+65
-13
lines changed

5 files changed

+65
-13
lines changed
 

‎NEWS.md

+8
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ This section lists changes that do not have deprecation warnings.
155155
* Worker-worker connections are setup lazily for an `:all_to_all` topology. Use keyword
156156
arg `lazy=false` to force all connections to be setup during a `addprocs` call. ([#22814])
157157

158+
* `^(A::AbstractMatrix{<:Integer}, p::Integer)` now throws a `DomainError`
159+
if `p < 0`, unless `A == one(A)` or `A == -one(A)` (same as for
160+
`^(A::Integer, p::Integer)`) ([#23366]).
161+
162+
* `^(A::AbstractMatrix{<:Integer}, p::Integer)` now promotes the element type in the same
163+
way as `^(A::Integer, p::Integer)`. This means, for instance, that `[1 1; 0 1]^big(1)`
164+
will return a `Matrix{BigInt}` instead of a `Matrix{Int}` ([#23366]).
165+
158166
Library improvements
159167
--------------------
160168

‎base/intfuncs.jl

+16-9
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,19 @@ end
159159
invmod(n::Integer, m::Integer) = invmod(promote(n,m)...)
160160

161161
# ^ for any x supporting *
162-
to_power_type(x::Number) = oftype(x*x, x)
163-
to_power_type(x) = x
164-
@noinline throw_domerr_powbysq(p) = throw(DomainError(p,
162+
to_power_type(x) = convert(promote_op(*, typeof(x), typeof(x)), x)
163+
@noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p,
165164
string("Cannot raise an integer x to a negative power ", p, '.',
166-
"\nMake x a float by adding a zero decimal (e.g., 2.0^$p instead ",
167-
"of 2^$p), or write 1/x^$(-p), float(x)^$p, or (x//1)^$p")))
165+
"\nConvert input to float.")))
166+
@noinline throw_domerr_powbysq(::Integer, p) = throw(DomainError(p,
167+
string("Cannot raise an integer x to a negative power ", p, '.',
168+
"\nMake x a float by adding a zero decimal (e.g., 2.0^$p instead ",
169+
"of 2^$p), or write 1/x^$(-p), float(x)^$p, or (x//1)^$p")))
170+
@noinline throw_domerr_powbysq(::AbstractMatrix, p) = throw(DomainError(p,
171+
string("Cannot raise an integer matrix x to a negative power ", p, '.',
172+
"\nMake x a float matrix by adding a zero decimal ",
173+
"(e.g., [2.0 1.0;1.0 0.0]^$p instead ",
174+
"of [2 1;1 0]^$p), or write float(x)^$p or Rational.(x)^$p")))
168175
function power_by_squaring(x_, p::Integer)
169176
x = to_power_type(x_)
170177
if p == 1
@@ -174,9 +181,9 @@ function power_by_squaring(x_, p::Integer)
174181
elseif p == 2
175182
return x*x
176183
elseif p < 0
177-
x == 1 && return copy(x)
178-
x == -1 && return iseven(p) ? one(x) : copy(x)
179-
throw_domerr_powbysq(p)
184+
isone(x) && return copy(x)
185+
isone(-x) && return iseven(p) ? one(x) : copy(x)
186+
throw_domerr_powbysq(x, p)
180187
end
181188
t = trailing_zeros(p) + 1
182189
p >>= t
@@ -196,7 +203,7 @@ function power_by_squaring(x_, p::Integer)
196203
end
197204
power_by_squaring(x::Bool, p::Unsigned) = ((p==0) | x)
198205
function power_by_squaring(x::Bool, p::Integer)
199-
p < 0 && !x && throw_domerr_powbysq(p)
206+
p < 0 && !x && throw_domerr_powbysq(x, p)
200207
return (p==0) | x
201208
end
202209

‎base/linalg/dense.jl

+8-2
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,15 @@ kron(a::AbstractMatrix, b::AbstractVector) = kron(a, reshape(b, length(b), 1))
357357
kron(a::AbstractVector, b::AbstractMatrix) = kron(reshape(a, length(a), 1), b)
358358

359359
# Matrix power
360-
(^)(A::AbstractMatrix, p::Integer) = p < 0 ? Base.power_by_squaring(inv(A), -p) : Base.power_by_squaring(A, p)
360+
(^)(A::AbstractMatrix, p::Integer) = p < 0 ? power_by_squaring(inv(A), -p) : power_by_squaring(A, p)
361+
function (^)(A::AbstractMatrix{T}, p::Integer) where T<:Integer
362+
# make sure that e.g. [1 1;1 0]^big(3)
363+
# gets promotes in a similar way as 2^big(3)
364+
TT = promote_op(^, T, typeof(p))
365+
return power_by_squaring(convert(AbstractMatrix{TT}, A), p)
366+
end
361367
function integerpow(A::AbstractMatrix{T}, p) where T
362-
TT = Base.promote_op(^, T, typeof(p))
368+
TT = promote_op(^, T, typeof(p))
363369
return (TT == T ? A : copy!(similar(A, TT), A))^Integer(p)
364370
end
365371
function schurpow(A::AbstractMatrix, p)

‎test/linalg/dense.jl

+23
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,29 @@ end
582582
end
583583
end
584584

585+
@testset "issue #23366 (Int Matrix to Int power)" begin
586+
@testset "Tests for $elty" for elty in (Int128, Int16, Int32, Int64, Int8,
587+
UInt128, UInt16, UInt32, UInt64, UInt8,
588+
BigInt)
589+
@test_throws DomainError elty[1 1;1 0]^-2
590+
@test (@inferred elty[1 1;1 0]^2) == elty[2 1;1 1]
591+
I_ = elty[1 0;0 1]
592+
@test I_^-1 == I_
593+
if !(elty<:Unsigned)
594+
@test (@inferred (-I_)^-1) == -I_
595+
@test (@inferred (-I_)^-2) == I_
596+
end
597+
# make sure that type promotion for ^(::Matrix{<:Integer}, ::Integer)
598+
# is analogous to type promotion for ^(::Integer, ::Integer)
599+
# e.g. [1 1;1 0]^big(10000) should return Matrix{BigInt}, the same
600+
# way as 2^big(10000) returns BigInt
601+
for elty2 = (Int64, BigInt)
602+
TT = Base.promote_op(^, elty, elty2)
603+
@test (@inferred elty[1 1;1 0]^elty2(1))::Matrix{TT} == [1 1;1 0]
604+
end
605+
end
606+
end
607+
585608
@testset "Least squares solutions" begin
586609
a = [ones(20) 1:20 1:20]
587610
b = reshape(eye(8, 5), 20, 2)

‎test/linalg/symmetric.jl

+10-2
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,18 @@ end
245245
@testset "pow" begin
246246
# Integer power
247247
@test (asym)^2 (Symmetric(asym)^2)::Symmetric
248-
@test (asym)^-2 (Symmetric(asym)^-2)::Symmetric
248+
if eltya <: Integer && !isone(asym) && !isone(-asym)
249+
@test_throws DomainError (asym)^-2
250+
else
251+
@test (asym)^-2 (Symmetric(asym)^-2)::Symmetric
252+
end
249253
@test (aposs)^2 (Symmetric(aposs)^2)::Symmetric
250254
@test (aherm)^2 (Hermitian(aherm)^2)::Hermitian
251-
@test (aherm)^-2 (Hermitian(aherm)^-2)::Hermitian
255+
if eltya <: Integer && !isone(aherm) && !isone(-aherm)
256+
@test_throws DomainError (aherm)^-2
257+
else
258+
@test (aherm)^-2 (Hermitian(aherm)^-2)::Hermitian
259+
end
252260
@test (apos)^2 (Hermitian(apos)^2)::Hermitian
253261
# integer floating point power
254262
@test (asym)^2.0 (Symmetric(asym)^2.0)::Symmetric

0 commit comments

Comments
 (0)
Please sign in to comment.