Skip to content

Commit 111325e

Browse files
authored
Add more inverses (#19)
1 parent 9a52bfc commit 111325e

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "InverseFunctions"
22
uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
3-
version = "0.1.4"
3+
version = "0.1.5"
44

55
[deps]
66
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

src/functions.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,15 @@ function square(x::Real)
1010
x < zero(x) && throw(DomainError(x, "`square` is defined as the inverse of `sqrt` and can only be evaluated for non-negative values"))
1111
return x^2
1212
end
13+
14+
15+
invpow2(x::Real, p::Integer) = sign(x) * abs(x)^inv(p)
16+
invpow2(x::Real, p::Real) = x zero(x) ? x^inv(p) : throw(DomainError(x, "inverse for x^$p is not defined at $x"))
17+
invpow2(x, p) = x^inv(p)
18+
19+
invpow1(b, x) = log(abs(b), abs(x))
20+
21+
invlog1(b::Real, x::Real) = b zero(b) && x zero(x) ? b^x : throw(DomainError(x, "inverse for log($b, x) is not defined at $x"))
22+
invlog1(b, x) = b^x
23+
24+
invlog2(b, x) = x^inv(b)

src/inverse.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,26 @@ inverse(::typeof(identity)) = identity
8484
inverse(::typeof(inv)) = inv
8585
inverse(::typeof(adjoint)) = adjoint
8686
inverse(::typeof(transpose)) = transpose
87+
inverse(::typeof(conj)) = conj
8788

89+
inverse(::typeof(!)) = !
8890
inverse(::typeof(+)) = +
8991
inverse(::typeof(-)) = -
9092

93+
inverse(f::Base.Fix1{typeof(+)}) = Base.Fix2(-, f.x)
94+
inverse(f::Base.Fix2{typeof(+)}) = Base.Fix2(-, f.x)
95+
inverse(f::Base.Fix1{typeof(-)}) = Base.Fix1(-, f.x)
96+
inverse(f::Base.Fix2{typeof(-)}) = Base.Fix1(+, f.x)
97+
inverse(f::Base.Fix1{typeof(*)}) = iszero(f.x) ? throw(DomainError(f.x, "Cannot invert multiplication by zero")) : Base.Fix1(\, f.x)
98+
inverse(f::Base.Fix2{typeof(*)}) = iszero(f.x) ? throw(DomainError(f.x, "Cannot invert multiplication by zero")) : Base.Fix2(/, f.x)
99+
inverse(f::Base.Fix1{typeof(/)}) = Base.Fix2(\, f.x)
100+
inverse(f::Base.Fix2{typeof(/)}) = Base.Fix2(*, f.x)
101+
inverse(f::Base.Fix1{typeof(\)}) = Base.Fix1(*, f.x)
102+
inverse(f::Base.Fix2{typeof(\)}) = Base.Fix1(/, f.x)
103+
104+
inverse(::typeof(deg2rad)) = rad2deg
105+
inverse(::typeof(rad2deg)) = deg2rad
106+
91107
inverse(::typeof(exp)) = log
92108
inverse(::typeof(log)) = exp
93109

@@ -102,3 +118,13 @@ inverse(::typeof(log1p)) = expm1
102118

103119
inverse(::typeof(sqrt)) = square
104120
inverse(::typeof(square)) = sqrt
121+
122+
inverse(::typeof(cbrt)) = Base.Fix2(^, 3)
123+
inverse(f::Base.Fix2{typeof(^)}) = iszero(f.x) ? throw(DomainError(f.x, "Cannot invert x^$(f.x)")) : Base.Fix2(invpow2, f.x)
124+
inverse(f::Base.Fix2{typeof(invpow2)}) = Base.Fix2(^, f.x)
125+
inverse(f::Base.Fix1{typeof(^)}) = Base.Fix1(invpow1, f.x)
126+
inverse(f::Base.Fix1{typeof(invpow1)}) = Base.Fix1(^, f.x)
127+
inverse(f::Base.Fix1{typeof(log)}) = Base.Fix1(invlog1, f.x)
128+
inverse(f::Base.Fix1{typeof(invlog1)}) = Base.Fix1(log, f.x)
129+
inverse(f::Base.Fix2{typeof(log)}) = Base.Fix2(invlog2, f.x)
130+
inverse(f::Base.Fix2{typeof(invlog2)}) = Base.Fix2(log, f.x)

test/test_inverse.jl

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,43 @@ InverseFunctions.inverse(f::Bar) = Bar(inv(f.A))
2828

2929
InverseFunctions.test_inverse(inverse, log, compare = ===)
3030

31+
InverseFunctions.test_inverse(!, false)
32+
3133
x = rand()
32-
for f in (foo, inv_foo, +, -, exp, log, exp2, log2, exp10, log10, expm1, log1p, sqrt)
34+
for f in (
35+
foo, inv_foo, log, log2, log10, log1p, sqrt,
36+
Base.Fix2(^, rand()), Base.Fix2(^, rand([-10:-1; 1:10])), Base.Fix1(^, rand()), Base.Fix1(log, rand()), Base.Fix2(log, rand()),
37+
)
38+
InverseFunctions.test_inverse(f, x)
39+
end
40+
for f in (
41+
+, -, exp, exp2, exp10, expm1, cbrt, deg2rad, rad2deg, conj,
42+
Base.Fix1(+, rand()), Base.Fix2(+, rand()), Base.Fix1(-, rand()), Base.Fix2(-, rand()),
43+
Base.Fix1(*, rand()), Base.Fix2(*, rand()), Base.Fix1(/, rand()), Base.Fix2(/, rand()), Base.Fix1(\, rand()), Base.Fix2(\, rand()),
44+
Base.Fix2(^, rand(-11:2:11)),
45+
)
3346
InverseFunctions.test_inverse(f, x)
47+
InverseFunctions.test_inverse(f, -x)
3448
end
49+
InverseFunctions.test_inverse(conj, 2 - 3im)
50+
51+
# ensure that inverses have domains compatible with original functions
52+
@test_throws DomainError inverse(Base.Fix1(*, 0))
53+
@test_throws DomainError inverse(Base.Fix2(^, 0))
54+
@test_throws DomainError inverse(Base.Fix1(log, 2))(-5)
55+
InverseFunctions.test_inverse(Base.Fix1(log, 2), -5 + 0im)
56+
@test_throws DomainError inverse(Base.Fix2(^, 0.5))(-5)
57+
InverseFunctions.test_inverse(Base.Fix2(^, 0.5), -5 + 0im)
3558

3659
A = rand(5, 5)
37-
for f in (identity, inv, adjoint, transpose)
60+
for f in (
61+
identity, inv, adjoint, transpose,
62+
log, sqrt, +, -, exp,
63+
Base.Fix1(+, rand(5, 5)), Base.Fix2(+, rand(5, 5)), Base.Fix1(-, rand(5, 5)), Base.Fix2(-, rand(5, 5)),
64+
Base.Fix1(*, rand()), Base.Fix2(*, rand()), Base.Fix1(*, rand(5, 5)), Base.Fix2(*, rand(5, 5)),
65+
Base.Fix2(/, rand()), Base.Fix1(/, rand(5, 5)), Base.Fix2(/, rand(5, 5)),
66+
Base.Fix1(\, rand()), Base.Fix1(\, rand(5, 5)), Base.Fix2(\, rand(5, 5)),
67+
)
3868
InverseFunctions.test_inverse(f, A)
3969
end
4070

0 commit comments

Comments
 (0)