Skip to content

Commit

Permalink
Add setinverse (#25)
Browse files Browse the repository at this point in the history
Co-authored-by: Chad Scherrer <[email protected]>
Co-authored-by: David Widmann <[email protected]>
  • Loading branch information
3 people authored Sep 28, 2022
1 parent 0e4a682 commit dd6e408
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 1 deletion.
4 changes: 3 additions & 1 deletion docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
```@docs
inverse
NoInverse
setinverse
```

## Test utility
Expand All @@ -13,8 +14,9 @@ NoInverse
InverseFunctions.test_inverse
```

## Additional functions
## Additional functionality

```@docs
InverseFunctions.square
InverseFunctions.FunctionWithInverse
```
1 change: 1 addition & 0 deletions src/InverseFunctions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ using Test

include("functions.jl")
include("inverse.jl")
include("setinverse.jl")
include("test.jl")

end # module
54 changes: 54 additions & 0 deletions src/setinverse.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# This file is a part of InverseFunctions.jl, licensed under the MIT License (MIT).


"""
struct FunctionWithInverse{F,InvF} <: Function
A function with an inverse.
Do not construct directly, use [`setinverse(f, invf)`](@ref) instead.
"""
struct FunctionWithInverse{F,InvF} <: Function
f::F
invf::InvF
end


(f::FunctionWithInverse)(x) = f.f(x)

inverse(f::FunctionWithInverse) = setinverse(f.invf, f.f)


"""
setinverse(f, invf)
Return a function that behaves like `f` and uses `invf` as its inverse.
Useful in cases where no inverse is defined for `f` or to set an inverse that
is only valid within a given context, e.g. only for a limited argument
range that is guaranteed by the use case but not in general.
For example, `asin` is not a valid inverse of `sin` for arbitrary arguments
of `sin`, but can be a valid inverse if the use case guarantees that the
argument of `sin` will always be within `-π` and `π`:
```jldoctest
julia> foo = setinverse(sin, asin);
julia> x = π/3;
julia> foo(x) == sin(x)
true
julia> inverse(foo)(foo(x)) ≈ x
true
julia> inverse(foo) === setinverse(asin, sin)
true
```
"""
setinverse(f, invf) = FunctionWithInverse(_unwrap_f(f), _unwrap_f(invf))
export setinverse

_unwrap_f(f) = f
_unwrap_f(f::FunctionWithInverse) = f.f
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Documenter
Test.@testset "Package InverseFunctions" begin
include("test_functions.jl")
include("test_inverse.jl")
include("test_setinverse.jl")

# doctests
Documenter.DocMeta.setdocmeta!(
Expand Down
15 changes: 15 additions & 0 deletions test/test_setinverse.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file is a part of InverseFunctions.jl, licensed under the MIT License (MIT).

using Test
using InverseFunctions


@testset "setinverse" begin
@test @inferred(setinverse(sin, asin)) === InverseFunctions.FunctionWithInverse(sin, asin)
@test @inferred(setinverse(sin, setinverse(asin, sqrt))) === InverseFunctions.FunctionWithInverse(sin, asin)
@test @inferred(setinverse(setinverse(sin, sqrt), asin)) === InverseFunctions.FunctionWithInverse(sin, asin)
@test @inferred(setinverse(setinverse(sin, asin), setinverse(asin, sqrt))) === InverseFunctions.FunctionWithInverse(sin, asin)

InverseFunctions.test_inverse(setinverse(sin, asin), π/4)
InverseFunctions.test_inverse(setinverse(asin, sin), 0.5)
end

0 comments on commit dd6e408

Please sign in to comment.