Skip to content

Commit dd6e408

Browse files
oschulzcscherrerdevmotion
authored
Add setinverse (#25)
Co-authored-by: Chad Scherrer <[email protected]> Co-authored-by: David Widmann <[email protected]>
1 parent 0e4a682 commit dd6e408

File tree

5 files changed

+74
-1
lines changed

5 files changed

+74
-1
lines changed

docs/src/api.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
```@docs
66
inverse
77
NoInverse
8+
setinverse
89
```
910

1011
## Test utility
@@ -13,8 +14,9 @@ NoInverse
1314
InverseFunctions.test_inverse
1415
```
1516

16-
## Additional functions
17+
## Additional functionality
1718

1819
```@docs
1920
InverseFunctions.square
21+
InverseFunctions.FunctionWithInverse
2022
```

src/InverseFunctions.jl

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ using Test
1010

1111
include("functions.jl")
1212
include("inverse.jl")
13+
include("setinverse.jl")
1314
include("test.jl")
1415

1516
end # module

src/setinverse.jl

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# This file is a part of InverseFunctions.jl, licensed under the MIT License (MIT).
2+
3+
4+
"""
5+
struct FunctionWithInverse{F,InvF} <: Function
6+
7+
A function with an inverse.
8+
9+
Do not construct directly, use [`setinverse(f, invf)`](@ref) instead.
10+
"""
11+
struct FunctionWithInverse{F,InvF} <: Function
12+
f::F
13+
invf::InvF
14+
end
15+
16+
17+
(f::FunctionWithInverse)(x) = f.f(x)
18+
19+
inverse(f::FunctionWithInverse) = setinverse(f.invf, f.f)
20+
21+
22+
"""
23+
setinverse(f, invf)
24+
25+
Return a function that behaves like `f` and uses `invf` as its inverse.
26+
27+
Useful in cases where no inverse is defined for `f` or to set an inverse that
28+
is only valid within a given context, e.g. only for a limited argument
29+
range that is guaranteed by the use case but not in general.
30+
31+
For example, `asin` is not a valid inverse of `sin` for arbitrary arguments
32+
of `sin`, but can be a valid inverse if the use case guarantees that the
33+
argument of `sin` will always be within `-π` and `π`:
34+
35+
```jldoctest
36+
julia> foo = setinverse(sin, asin);
37+
38+
julia> x = π/3;
39+
40+
julia> foo(x) == sin(x)
41+
true
42+
43+
julia> inverse(foo)(foo(x)) ≈ x
44+
true
45+
46+
julia> inverse(foo) === setinverse(asin, sin)
47+
true
48+
```
49+
"""
50+
setinverse(f, invf) = FunctionWithInverse(_unwrap_f(f), _unwrap_f(invf))
51+
export setinverse
52+
53+
_unwrap_f(f) = f
54+
_unwrap_f(f::FunctionWithInverse) = f.f

test/runtests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Documenter
77
Test.@testset "Package InverseFunctions" begin
88
include("test_functions.jl")
99
include("test_inverse.jl")
10+
include("test_setinverse.jl")
1011

1112
# doctests
1213
Documenter.DocMeta.setdocmeta!(

test/test_setinverse.jl

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# This file is a part of InverseFunctions.jl, licensed under the MIT License (MIT).
2+
3+
using Test
4+
using InverseFunctions
5+
6+
7+
@testset "setinverse" begin
8+
@test @inferred(setinverse(sin, asin)) === InverseFunctions.FunctionWithInverse(sin, asin)
9+
@test @inferred(setinverse(sin, setinverse(asin, sqrt))) === InverseFunctions.FunctionWithInverse(sin, asin)
10+
@test @inferred(setinverse(setinverse(sin, sqrt), asin)) === InverseFunctions.FunctionWithInverse(sin, asin)
11+
@test @inferred(setinverse(setinverse(sin, asin), setinverse(asin, sqrt))) === InverseFunctions.FunctionWithInverse(sin, asin)
12+
13+
InverseFunctions.test_inverse(setinverse(sin, asin), π/4)
14+
InverseFunctions.test_inverse(setinverse(asin, sin), 0.5)
15+
end

0 commit comments

Comments
 (0)