An idea: A function finv
that returns the functional inverse of a function. For instance
finv(::typeof(cos)) = acos
I thought of this not for cos
, but for a particular function whose inverse does not have a recognized name nor a conventional computer function name like arccosine → acos
.
Two possible uses:
- discoverability in the case that
myfunc
is well known, but there is no standard name for the inverse. Instead of remembering myfunc_inv(x)
or invmyfunc(x)
or whatever, it is always `finv(myfunc)(x).
- To get the functional inverse of an unknown function. Maybe in a computation similar to automatic differentiation.
Possible complications are how to handle multiple branches and multiple arguments.
Are there outstanding cases where this would be useful, or is my use case more or less isolated ? A related question, is there already a convention used in some packages that I have not discovered?
4 Likes
Sure, this is definitely something you could implement for specific functions, although I would guess that the majority of functions used in practical code don’t have well-defined inverses. But I think your idea of implementing finv(::typeof(f))
for various functions f
is sound.
On the other hand, the existence of a computation that could invert an arbitrary function efficiently would, at the very least, prove P = NP and probably render all of computer science obsolete. So that part’s probably not going to happen 
7 Likes
To get the functional inverse of an unknown function. Maybe in a computation similar to automatic differentiation.
A package that implements the analysis-stack of inverse function theorem / implicit function theorem via AD + Newton + symbolic shortcuts when feasible would be really cool, but I know of no precedent of any library/language doing this well. Unfortunately this sounds like a really big project (if some terms in your function are themselves implicit functions, then you need to collapse the stack and use shortcuts).
While we are obviously not going to have an algorithm that does functional inverse of cryptographic hash functions, I agree that it would be useful to just have a function where the inverses are explicitly defined on everything you can write an inverse for. I even seem to remember having written an API for a package that required people to define inv(::typeof(f))
for functions they want to use.
By the way, you should be able to just use inv
, I don’t see any reason why it would need a special name as it doesn’t have methods for specific functions like that.
2 Likes
Well my main motivation was discoverability and maybe reducing verb count, rather than dealing comprehensively with functional routines. For instance
finv(::typeof(lambertw))= z -> z*exp(z)
And I have at least one other use case.
But, if you really think there are algorithms that would like to use routines that produce numerical functional inverses, you could do something like this.
function nofunc end
finv(f::Function) = nofunc
Then your algorithm that wants inverses could include a catchall method for nofunc
, and optimized methods for those functions that have defined a method for finv
.
1 Like
There’s also this cute trick:
julia> Base.literal_pow(::typeof(^), ::typeof(sin), ::Val{-1}) = asin # edited silly typo
julia> (sin^-1)(1)
1.5707963267948966
4 Likes
This is exactly what i guessed. It has been done before, and may as well be made general. But, inv
is used for multiplicative inverse (and its generalization to matrices) which is a different concept. And inv
is in base. In any case, the most important thing is to have a 500 post discussion about whether it belongs in base and which concept gets primacy for using inv

3 Likes
That is too temptingly cute. (but you meant asin
)… Or on second thought, it is more obvious what (sin^-1)(x)
means than what finv(sin)(x)
means.
1 Like
I don’t think the literal_pow
thing is such a good idea, because then it really does look like a multiplicative inverse. Same reason I use \arccos(x) instead of \cos^{-1}(x).
It may not be exactly what you are looking for, but I think the most general way to do this is with interval arithmetic, i.e. https://github.com/JuliaIntervals/IntervalContractors.jl Which is a combination of analytics and provable numerical methods on intervals.
Guess it depends on the application, though… But if you don’t know about JuliaIntervals
, it is a fun read nevertheless!
2 Likes
I agree with you for written math. You’ve seen early 20th century papers with sin x
, etc. And the same thought went through my head just now. But in the code you need parens, which disambiguate: (sin^-1)(x)
.
I still think it’s more common not to use parentheses for functions with \textrm
text. I personally like them to have parentheses.
That looks cool. David’s interval project is great. But, I have to say, I don’t like cos!
, because !
does not mean mutating function in this case. … we are drifting. That’s ok. Hendrix’s drifting was looping for an hour or so this morning.
You can define such a type of function with Reduce.jl symbolic package:
julia> using Reduce
julia> finv(f::Function) = collect(Algebra.solve(:($f(x) == y),:x))
finv (generic function with 1 method)
julia> finv(cos)
2-element Array{Expr,1}:
:(x = acos(y) + 2 * arbint(5) * π)
:(x = -((acos(y) - 2 * arbint(5) * π)))
Also, to select the principal branch, set allbranch
to off
julia> allbranch(false)
false
julia> finv(cos)
1-element Array{Expr,1}:
:(x = acos(y))
5 Likes
Alternatively, if you want to also evaluate the function at x
, you can do it like this
julia> using Reduce
julia> allbranch(false)
false
julia> @generated function finv(x,::Val{f}) where f
out = Algebra.solve(:($f(y) == x),:y)
:(@fastmath $(out[1].args[2]))
end
finv (generic function with 1 method)
julia> finv(x,f::Function) = finv(x,Val(f))
finv (generic function with 2 methods)
julia> finv(1,sin)
1.5707963267948966
This allows you to evaluate the inverse at any x
of any function f
that is invertible.
4 Likes
Such inverses bound to function names have nice applications in pattern matching and destructuring assignmens,
@match Polar(r, phi) = z
Old issue:
https://github.com/kmsquire/Match.jl/issues/15
1 Like
Those are some interesting ideas. But, adding a dependency, especially a binary dependency, is not a good option for something so simple.
For the moment, I have decided on finv
and pushed changes to github. For LambertW.jl
, I have
finv(::typeof(lambertw)) = z -> z * exp(z)
For EmpiricalCDFs.jl, the empirical CDF is a callable object. I use essentially the following
function finv(cdf::EmpiricalCDF)
function (c::Real)
_inverse(cdf,c)
end
end
These seem consistent and generalize well.
1 Like