From e6826aa8acc5704ffcd70ff09369969f6a036817 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 23 Sep 2022 16:30:12 +0200 Subject: [PATCH 1/6] Add setinverse --- src/InverseFunctions.jl | 1 + src/setinverse.jl | 57 +++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + test/test_setinverse.jl | 15 +++++++++++ 4 files changed, 74 insertions(+) create mode 100644 src/setinverse.jl create mode 100644 test/test_setinverse.jl diff --git a/src/InverseFunctions.jl b/src/InverseFunctions.jl index c22608e..0c5438b 100644 --- a/src/InverseFunctions.jl +++ b/src/InverseFunctions.jl @@ -10,6 +10,7 @@ using Test include("functions.jl") include("inverse.jl") +include("setinverse.jl") include("test.jl") end # module diff --git a/src/setinverse.jl b/src/setinverse.jl new file mode 100644 index 0000000..79344a5 --- /dev/null +++ b/src/setinverse.jl @@ -0,0 +1,57 @@ +# 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) = FunctionWithInverse(f.invf, f.f) + + +""" + setinverse(f, invf)::Function + +Returns a function that behaves like `f` and uses `invf` it implement it's +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. for only for a limited argument +range that is guaranteed by the use case but not in general. + +For example, `asin` not is 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 +``` +""" +function setinverse end +export setinverse + +setinverse(f, invf) = FunctionWithInverse(f, invf) +setinverse(f::FunctionWithInverse, invf) = FunctionWithInverse(f.f, invf) +setinverse(f, invf::FunctionWithInverse) = FunctionWithInverse(f, invf.f) +setinverse(f::FunctionWithInverse, invf::FunctionWithInverse) = FunctionWithInverse(f.f, invf.f) diff --git a/test/runtests.jl b/test/runtests.jl index b076cff..58ee692 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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!( diff --git a/test/test_setinverse.jl b/test/test_setinverse.jl new file mode 100644 index 0000000..50b97ef --- /dev/null +++ b/test/test_setinverse.jl @@ -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 From c84aa3674fc60703352749e0c6e62b14d0281682 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 23 Sep 2022 16:42:31 +0200 Subject: [PATCH 2/6] Fix docstring of setinverse Co-authored-by: Chad Scherrer --- src/setinverse.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setinverse.jl b/src/setinverse.jl index 79344a5..c587cd5 100644 --- a/src/setinverse.jl +++ b/src/setinverse.jl @@ -31,7 +31,7 @@ range that is guaranteed by the use case but not in general. For example, `asin` not is 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 `π`:` +argument of `sin` will always be within `-π` and `π`: ```jldoctest julia> foo = setinverse(sin, asin); From f3c705308d34939bfc9dd50b1f09d9b6f963b88d Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 23 Sep 2022 16:42:48 +0200 Subject: [PATCH 3/6] Fix docstring of setinverse Co-authored-by: Chad Scherrer --- src/setinverse.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setinverse.jl b/src/setinverse.jl index c587cd5..1751acd 100644 --- a/src/setinverse.jl +++ b/src/setinverse.jl @@ -22,7 +22,7 @@ inverse(f::FunctionWithInverse) = FunctionWithInverse(f.invf, f.f) """ setinverse(f, invf)::Function -Returns a function that behaves like `f` and uses `invf` it implement it's +Returns a function that behaves like `f` and uses `invf` it implement its inverse. Useful in cases where no inverse is defined for `f` or to set an inverse that From c4c2c833f80c86bcdcbcd1425851861699e4f973 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 23 Sep 2022 16:49:57 +0200 Subject: [PATCH 4/6] Fix docs gen --- docs/src/api.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/api.md b/docs/src/api.md index fecb4b7..e34de90 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -5,6 +5,7 @@ ```@docs inverse NoInverse +setinverse ``` ## Test utility @@ -13,8 +14,9 @@ NoInverse InverseFunctions.test_inverse ``` -## Additional functions +## Additional functionality ```@docs InverseFunctions.square +InverseFunctions.FunctionWithInverse ``` From 18051bdace0a9a26e6070a63ab2690506b68af3d Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 24 Sep 2022 15:48:50 +0200 Subject: [PATCH 5/6] Improve setinverse docs and implementation Co-authored-by: David Widmann --- src/setinverse.jl | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/setinverse.jl b/src/setinverse.jl index 1751acd..de1d7ff 100644 --- a/src/setinverse.jl +++ b/src/setinverse.jl @@ -2,7 +2,7 @@ """ - struct FunctionWithInverse{F,InvF}::Function + struct FunctionWithInverse{F,InvF} <: Function A function with an inverse. @@ -16,20 +16,19 @@ end (f::FunctionWithInverse)(x) = f.f(x) -inverse(f::FunctionWithInverse) = FunctionWithInverse(f.invf, f.f) +inverse(f::FunctionWithInverse) = setinverse(f.invf, f.f) """ - setinverse(f, invf)::Function + setinverse(f, invf) -Returns a function that behaves like `f` and uses `invf` it implement its -inverse. +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. for only for a limited argument +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` not is a valid inverse of `sin` for arbitrary arguments +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 `π`: @@ -51,7 +50,6 @@ true function setinverse end export setinverse -setinverse(f, invf) = FunctionWithInverse(f, invf) -setinverse(f::FunctionWithInverse, invf) = FunctionWithInverse(f.f, invf) -setinverse(f, invf::FunctionWithInverse) = FunctionWithInverse(f, invf.f) -setinverse(f::FunctionWithInverse, invf::FunctionWithInverse) = FunctionWithInverse(f.f, invf.f) +_unwrap_f(f) = f +_unwrap_f(f::FunctionWithInverse) = f.f +setinverse(f, invf) = FunctionWithInverse(_unwrap_f(f), _unwrap_f(invf)) From 62bca9b66697028896947ea4d712234a121218c6 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 25 Sep 2022 09:34:55 +0200 Subject: [PATCH 6/6] Shorten definition of setinverse Co-authored-by: David Widmann --- src/setinverse.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/setinverse.jl b/src/setinverse.jl index de1d7ff..b027c53 100644 --- a/src/setinverse.jl +++ b/src/setinverse.jl @@ -47,9 +47,8 @@ julia> inverse(foo) === setinverse(asin, sin) true ``` """ -function setinverse end +setinverse(f, invf) = FunctionWithInverse(_unwrap_f(f), _unwrap_f(invf)) export setinverse _unwrap_f(f) = f _unwrap_f(f::FunctionWithInverse) = f.f -setinverse(f, invf) = FunctionWithInverse(_unwrap_f(f), _unwrap_f(invf))