From ff3852cefe0705adb2017f5f572bcb17b0044f46 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Thu, 13 Jul 2023 12:20:01 +0300 Subject: [PATCH 01/10] fix inverse(^) domain --- src/functions.jl | 7 ++----- src/inverse.jl | 1 + test/test_inverse.jl | 6 ++++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/functions.jl b/src/functions.jl index 6866a50..b6c2f42 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -13,11 +13,8 @@ end function invpow2(x::Real, p::Integer) - if x ≥ zero(x) || isodd(p) - copysign(abs(x)^inv(p), x) - else - throw(DomainError(x, "inverse for x^$p is not defined at $x")) - end + @assert isodd(p) + copysign(abs(x)^inv(p), x) end function invpow2(x::Real, p::Real) if x ≥ zero(x) diff --git a/src/inverse.jl b/src/inverse.jl index 624fd9f..1dd71ac 100644 --- a/src/inverse.jl +++ b/src/inverse.jl @@ -148,6 +148,7 @@ inverse(::typeof(square)) = sqrt inverse(::typeof(cbrt)) = Base.Fix2(^, 3) inverse(f::Base.Fix2{typeof(^)}) = iszero(f.x) ? throw(DomainError(f.x, "Cannot invert x^$(f.x)")) : Base.Fix2(invpow2, f.x) +inverse(f::Base.Fix2{typeof(^), <:Integer}) = iseven(f.x) ? throw(DomainError(f.x, "Cannot invert x^$(f.x)")) : Base.Fix2(invpow2, f.x) inverse(f::Base.Fix2{typeof(invpow2)}) = Base.Fix2(^, f.x) inverse(f::Base.Fix1{typeof(^)}) = Base.Fix1(invpow1, f.x) inverse(f::Base.Fix1{typeof(invpow1)}) = Base.Fix1(^, f.x) diff --git a/test/test_inverse.jl b/test/test_inverse.jl index 1f0f2e6..65b0576 100644 --- a/test/test_inverse.jl +++ b/test/test_inverse.jl @@ -46,7 +46,7 @@ InverseFunctions.inverse(f::Bar) = Bar(inv(f.A)) x = rand() for f in ( foo, inv_foo, log, log2, log10, log1p, sqrt, - Base.Fix2(^, rand()), Base.Fix2(^, rand([-10:-1; 1:10])), Base.Fix1(^, rand()), Base.Fix1(log, rand()), Base.Fix1(log, 1/rand()), Base.Fix2(log, rand()), + Base.Fix2(^, rand()), Base.Fix2(^, rand(float.([-10:-1; 1:10]))), Base.Fix1(^, rand()), Base.Fix1(log, rand()), Base.Fix1(log, 1/rand()), Base.Fix2(log, rand()), ) InverseFunctions.test_inverse(f, x) end @@ -80,7 +80,9 @@ InverseFunctions.inverse(f::Bar) = Bar(inv(f.A)) @test_throws DomainError inverse(Base.Fix2(^, 0.5))(-5) @test_throws DomainError inverse(Base.Fix2(^, 0.51))(complex(-5)) InverseFunctions.test_inverse(Base.Fix2(^, 0.5), complex(-5)) - @test_throws DomainError inverse(Base.Fix2(^, 2))(-5) + @test_throws DomainError inverse(Base.Fix2(^, 2)) + @test_throws DomainError inverse(Base.Fix2(^, -4)) + InverseFunctions.test_inverse(Base.Fix2(^, 2.0), 4) @test_throws DomainError inverse(Base.Fix1(^, 2))(-5) @test_throws DomainError inverse(Base.Fix1(^, -2))(3) @test_throws DomainError inverse(Base.Fix1(^, -2))(3) From ba8e13d7ef8f7debbeb4dc6c3d79c6101d90da32 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Sat, 25 Nov 2023 11:20:11 -0500 Subject: [PATCH 02/10] more inverse domain fixes --- src/functions.jl | 31 +++++++++++++++---------------- src/inverse.jl | 12 +++++++++--- test/test_inverse.jl | 2 +- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/functions.jl b/src/functions.jl index b6c2f42..2342b3e 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -13,26 +13,22 @@ end function invpow2(x::Real, p::Integer) - @assert isodd(p) + # exception should never happen in actual use: this check is done in inverse(f) + isodd(p) || throw(DomainError(x, "inverse for x^$p is not defined at $x")) copysign(abs(x)^inv(p), x) end function invpow2(x::Real, p::Real) - if x ≥ zero(x) - x^inv(p) - else - throw(DomainError(x, "inverse for x^$p is not defined at $x")) - end + x ≥ zero(x) || throw(DomainError(x, "inverse for x^$p is not defined at $x")) + x^inv(p) end function invpow2(x, p::Real) # complex x^p is only invertible for p = 1/n - if isinteger(inv(p)) - x^inv(p) - else - throw(DomainError(x, "inverse for x^$p is not defined at $x")) - end + isinteger(inv(p)) || throw(DomainError(x, "inverse for x^$p is not defined at $x")) + x^inv(p) end function invpow1(b::Real, x::Real) + # b < 0 should never happen in actual use: this check is done in inverse(f) if b ≥ zero(b) && x ≥ zero(x) log(b, x) else @@ -41,14 +37,17 @@ function invpow1(b::Real, x::Real) end function invlog1(b::Real, x::Real) - if b ≥ zero(b) - b^x - else - throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) - end + # exception may happen here: check cannot be done in inverse(f) because of log(Real, Complex) + b > zero(b) && b != one(b) || throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) + b^x end invlog1(b, x) = b^x +function invlog2(b::Real, x::Real) + # exception may happen here: check cannot be done in inverse(f) because of log(Complex, Real) + x > zero(x) && x != one(x) || throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) + x^inv(b) +end invlog2(b, x) = x^inv(b) diff --git a/src/inverse.jl b/src/inverse.jl index 1dd71ac..190773a 100644 --- a/src/inverse.jl +++ b/src/inverse.jl @@ -147,18 +147,24 @@ inverse(::typeof(sqrt)) = square inverse(::typeof(square)) = sqrt inverse(::typeof(cbrt)) = Base.Fix2(^, 3) + inverse(f::Base.Fix2{typeof(^)}) = iszero(f.x) ? throw(DomainError(f.x, "Cannot invert x^$(f.x)")) : Base.Fix2(invpow2, f.x) -inverse(f::Base.Fix2{typeof(^), <:Integer}) = iseven(f.x) ? throw(DomainError(f.x, "Cannot invert x^$(f.x)")) : Base.Fix2(invpow2, f.x) +inverse(f::Base.Fix2{typeof(^), <:Integer}) = isodd(f.x) ? Base.Fix2(invpow2, f.x) : throw(DomainError(f.x, "Cannot invert x^$(f.x)")) inverse(f::Base.Fix2{typeof(invpow2)}) = Base.Fix2(^, f.x) + +inverse(f::Base.Fix1{typeof(^), <:Real}) = f.x > zero(f.x) ? Base.Fix1(invpow1, f.x) : throw(DomainError(f.x, "Cannot invert")) inverse(f::Base.Fix1{typeof(^)}) = Base.Fix1(invpow1, f.x) inverse(f::Base.Fix1{typeof(invpow1)}) = Base.Fix1(^, f.x) -inverse(f::Base.Fix1{typeof(log)}) = Base.Fix1(invlog1, f.x) + +inverse(f::Base.Fix1{typeof(log)}) = f.x == one(f.x) ? throw(DomainError(f.x, "Cannot invert")) : Base.Fix1(invlog1, f.x) inverse(f::Base.Fix1{typeof(invlog1)}) = Base.Fix1(log, f.x) -inverse(f::Base.Fix2{typeof(log)}) = Base.Fix2(invlog2, f.x) + +inverse(f::Base.Fix2{typeof(log)}) = f.x == one(f.x) ? throw(DomainError(f.x, "Cannot invert")) : Base.Fix2(invlog2, f.x) inverse(f::Base.Fix2{typeof(invlog2)}) = Base.Fix2(log, f.x) inverse(f::Base.Fix2{typeof(divrem)}) = Base.Fix2(invdivrem, f.x) inverse(f::Base.Fix2{typeof(invdivrem)}) = Base.Fix2(divrem, f.x) + inverse(f::Base.Fix2{typeof(fldmod)}) = Base.Fix2(invfldmod, f.x) inverse(f::Base.Fix2{typeof(invfldmod)}) = Base.Fix2(fldmod, f.x) diff --git a/test/test_inverse.jl b/test/test_inverse.jl index 65b0576..e3c55d9 100644 --- a/test/test_inverse.jl +++ b/test/test_inverse.jl @@ -46,7 +46,7 @@ InverseFunctions.inverse(f::Bar) = Bar(inv(f.A)) x = rand() for f in ( foo, inv_foo, log, log2, log10, log1p, sqrt, - Base.Fix2(^, rand()), Base.Fix2(^, rand(float.([-10:-1; 1:10]))), Base.Fix1(^, rand()), Base.Fix1(log, rand()), Base.Fix1(log, 1/rand()), Base.Fix2(log, rand()), + Base.Fix2(^, 3*rand() - 0.5), Base.Fix2(^, rand(float.([-10:-1; 1:10]))), Base.Fix1(^, rand()), Base.Fix1(log, rand()), Base.Fix1(log, 1/rand()), Base.Fix2(log, rand()), ) InverseFunctions.test_inverse(f, x) end From a23b811a3832de6d5d021461e7a4d8c78f5cb97f Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Tue, 20 Aug 2024 19:58:46 -0400 Subject: [PATCH 03/10] Update src/functions.jl Co-authored-by: David Widmann --- src/functions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions.jl b/src/functions.jl index 2342b3e..f6fd768 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -38,7 +38,7 @@ end function invlog1(b::Real, x::Real) # exception may happen here: check cannot be done in inverse(f) because of log(Real, Complex) - b > zero(b) && b != one(b) || throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) + b > zero(b) && !isone(b) || throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) b^x end invlog1(b, x) = b^x From 3d4be83213dcb3b3f8e5474b322037707cb0c812 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Tue, 20 Aug 2024 19:58:56 -0400 Subject: [PATCH 04/10] Update src/functions.jl Co-authored-by: David Widmann --- src/functions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions.jl b/src/functions.jl index f6fd768..2850f1b 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -45,7 +45,7 @@ invlog1(b, x) = b^x function invlog2(b::Real, x::Real) # exception may happen here: check cannot be done in inverse(f) because of log(Complex, Real) - x > zero(x) && x != one(x) || throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) + x > zero(x) && !isone(x) || throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) x^inv(b) end invlog2(b, x) = x^inv(b) From 7db1ae31144a12a52cd8f38e43f434f937f69663 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Tue, 20 Aug 2024 19:59:21 -0400 Subject: [PATCH 05/10] Update src/inverse.jl Co-authored-by: David Widmann --- src/inverse.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inverse.jl b/src/inverse.jl index 190773a..b07b44e 100644 --- a/src/inverse.jl +++ b/src/inverse.jl @@ -156,7 +156,7 @@ inverse(f::Base.Fix1{typeof(^), <:Real}) = f.x > zero(f.x) ? Base.Fix1(invpow1, inverse(f::Base.Fix1{typeof(^)}) = Base.Fix1(invpow1, f.x) inverse(f::Base.Fix1{typeof(invpow1)}) = Base.Fix1(^, f.x) -inverse(f::Base.Fix1{typeof(log)}) = f.x == one(f.x) ? throw(DomainError(f.x, "Cannot invert")) : Base.Fix1(invlog1, f.x) +inverse(f::Base.Fix1{typeof(log)}) = isone(f.x) ? throw(DomainError(f.x, "Cannot invert $(f.x)^x")) : Base.Fix1(invlog1, f.x) inverse(f::Base.Fix1{typeof(invlog1)}) = Base.Fix1(log, f.x) inverse(f::Base.Fix2{typeof(log)}) = f.x == one(f.x) ? throw(DomainError(f.x, "Cannot invert")) : Base.Fix2(invlog2, f.x) From 415d4c39432e14d9ab009495cf85a0ad0174a9f1 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Tue, 20 Aug 2024 19:59:31 -0400 Subject: [PATCH 06/10] Update src/inverse.jl Co-authored-by: David Widmann --- src/inverse.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inverse.jl b/src/inverse.jl index b07b44e..71c7e3a 100644 --- a/src/inverse.jl +++ b/src/inverse.jl @@ -159,7 +159,7 @@ inverse(f::Base.Fix1{typeof(invpow1)}) = Base.Fix1(^, f.x) inverse(f::Base.Fix1{typeof(log)}) = isone(f.x) ? throw(DomainError(f.x, "Cannot invert $(f.x)^x")) : Base.Fix1(invlog1, f.x) inverse(f::Base.Fix1{typeof(invlog1)}) = Base.Fix1(log, f.x) -inverse(f::Base.Fix2{typeof(log)}) = f.x == one(f.x) ? throw(DomainError(f.x, "Cannot invert")) : Base.Fix2(invlog2, f.x) +inverse(f::Base.Fix2{typeof(log)}) = isone(f.x) ? throw(DomainError(f.x, "Cannot invert log(x, $(f.x))")) : Base.Fix2(invlog2, f.x) inverse(f::Base.Fix2{typeof(invlog2)}) = Base.Fix2(log, f.x) inverse(f::Base.Fix2{typeof(divrem)}) = Base.Fix2(invdivrem, f.x) From 157f7f084573ee737e53fec57c2af80475470a05 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Wed, 21 Aug 2024 01:01:03 +0100 Subject: [PATCH 07/10] fix --- src/inverse.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inverse.jl b/src/inverse.jl index 71c7e3a..a4ef335 100644 --- a/src/inverse.jl +++ b/src/inverse.jl @@ -152,11 +152,11 @@ inverse(f::Base.Fix2{typeof(^)}) = iszero(f.x) ? throw(DomainError(f.x, "Cannot inverse(f::Base.Fix2{typeof(^), <:Integer}) = isodd(f.x) ? Base.Fix2(invpow2, f.x) : throw(DomainError(f.x, "Cannot invert x^$(f.x)")) inverse(f::Base.Fix2{typeof(invpow2)}) = Base.Fix2(^, f.x) -inverse(f::Base.Fix1{typeof(^), <:Real}) = f.x > zero(f.x) ? Base.Fix1(invpow1, f.x) : throw(DomainError(f.x, "Cannot invert")) +inverse(f::Base.Fix1{typeof(^), <:Real}) = f.x > zero(f.x) ? Base.Fix1(invpow1, f.x) : throw(DomainError(f.x, "Cannot invert $(f.x)^x")) inverse(f::Base.Fix1{typeof(^)}) = Base.Fix1(invpow1, f.x) inverse(f::Base.Fix1{typeof(invpow1)}) = Base.Fix1(^, f.x) -inverse(f::Base.Fix1{typeof(log)}) = isone(f.x) ? throw(DomainError(f.x, "Cannot invert $(f.x)^x")) : Base.Fix1(invlog1, f.x) +inverse(f::Base.Fix1{typeof(log)}) = isone(f.x) ? throw(DomainError(f.x, "Cannot invert log($(f.x), x)")) : Base.Fix1(invlog1, f.x) inverse(f::Base.Fix1{typeof(invlog1)}) = Base.Fix1(log, f.x) inverse(f::Base.Fix2{typeof(log)}) = isone(f.x) ? throw(DomainError(f.x, "Cannot invert log(x, $(f.x))")) : Base.Fix2(invlog2, f.x) From 3f093b85e3e980ceacf24de6cb5a86a2b6a30310 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Fri, 13 Sep 2024 15:27:25 +0200 Subject: [PATCH 08/10] add tests --- test/test_inverse.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/test_inverse.jl b/test/test_inverse.jl index e3c55d9..face61f 100644 --- a/test/test_inverse.jl +++ b/test/test_inverse.jl @@ -83,6 +83,14 @@ InverseFunctions.inverse(f::Bar) = Bar(inv(f.A)) @test_throws DomainError inverse(Base.Fix2(^, 2)) @test_throws DomainError inverse(Base.Fix2(^, -4)) InverseFunctions.test_inverse(Base.Fix2(^, 2.0), 4) + @test_throws DomainError inverse(Base.Fix1(^, 2.0))(-4) + @test_throws DomainError inverse(Base.Fix1(^, -2.0))(4) + @test_throws DomainError inverse(Base.Fix1(^, 0))(4) + @test_throws DomainError inverse(Base.Fix1(log, -2))(4) + @test_throws DomainError inverse(Base.Fix1(log, 1))(4) + @test_throws DomainError inverse(Base.Fix2(^, 0))(4) + @test_throws DomainError inverse(Base.Fix2(log, -2))(4) + @test_throws DomainError inverse(Base.Fix2(log, 1))(4) @test_throws DomainError inverse(Base.Fix1(^, 2))(-5) @test_throws DomainError inverse(Base.Fix1(^, -2))(3) @test_throws DomainError inverse(Base.Fix1(^, -2))(3) From d99f067c56d1fdd0b08a8f9cab767fbbfa18eff5 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Sat, 14 Sep 2024 08:29:47 +0200 Subject: [PATCH 09/10] test --- test/test_inverse.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_inverse.jl b/test/test_inverse.jl index 6e82609..79cf725 100644 --- a/test/test_inverse.jl +++ b/test/test_inverse.jl @@ -154,11 +154,10 @@ end InverseFunctions.test_inverse(sqrt, x) @test_throws DomainError inverse(sqrt)(-x) - InverseFunctions.test_inverse(Base.Fix2(^, 2), x) - @test_throws DomainError inverse(Base.Fix2(^, 2))(-x) InverseFunctions.test_inverse(Base.Fix2(^, 3), x) InverseFunctions.test_inverse(Base.Fix2(^, 3), -x) InverseFunctions.test_inverse(Base.Fix2(^, -3.5), x) + @test_throws DomainError inverse(Base.Fix2(^, 2))(-x) end @testset "dates" begin From 50f3fcade10d8b4014856c51c4615163f9e48d47 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Mon, 16 Sep 2024 12:58:00 -0400 Subject: [PATCH 10/10] bump --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 23a09fe..b6acb6f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "InverseFunctions" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.16" +version = "0.1.17" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"