JuliaMath / HypergeometricFunctions.jl

A Julia package for calculating hypergeometric functions

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unify the function interfaces?

ararslan opened this issue · comments

The names used for the Gauss and confluent hypergeometric functions, _₂F₁ and _₁F₁, respectively, are rather difficult to type and are not particularly discoverable (IMO). Since the functions accept different numbers of arguments, we could unify the interfaces into a single function and use dispatch to differentiate them. This is analogous to the approach taken by, for example, gamma: one argument is the gamma function, two is the upper incomplete gamma.

I would advocate for the following:

  • Generalized: hypergeom(a::Collection, b::Collection, z) as an alternative to pFq. This matches Matlab's interface for the function.
  • Gauss: hypergeom(a::Scalar, b::Scalar, c::Scalar, z) for _₂F₁
  • Confluent: hypergeom(a::Scalar, b::Scalar, z) for _₁F₁
    • kummerm(a, b, z) for HypergeometricFunctions.M
    • tricomiu(a, b, z) for HypergeometricFunctions.U

That's not necessarily to replace the existing interface but could supplement it.

Thoughts?

I like it! Not sure this is better but another option is use kwds: hypergeom(z; a = ..., b = ..., c = ....) And so if any is missing its taken to be empty, eg 1F1(1, 2, z) would be hypergeom(z; a=1, b=2).

That's a good point as well. I personally find keyword arguments most useful when their names are meaningful, but I'm not sure that naming a, b, and c in each call provides much additional clarity. It also adds a layer of indirection if we want to use dispatch on the parameters' types. From that perspective I think it makes sense to use positional arguments but I don't feel too strongly about it.

I think I agree, the only reason I mention it is that when you specify only two scalars it's ambiguous which parameter is dropped. Perhaps hypergeom(z, a, b, c) And then its clear that hypergeom(z, a, b) is dropping the c parameter?

With two scalar parameters, the ambiguity could be resolved like hypergeom(a, b, z; type = (p, q)) where p + q == 2

Perhaps hypergeom(z, a, b, c) And then its clear that hypergeom(z, a, b) is dropping the c parameter?

Nice, I like that!

With two scalar parameters, the ambiguity could be resolved like hypergeom(a, b, z; type = (p, q)) where p + q == 2

I'm not sure I understand what you mean, could you elaborate? Do you mean the ambiguity in which confluent hypergeometric function is being used, i.e. Kummer vs. Tricomi?

There's two parameters in 1F1, 0F2, and (the divergent series) 2F0

Ah right, I see. I think those would be best handled by the method for collections, where the lengths of the collections dictate p and q. I'd imagine the more common 1F1 would correspond to the 2-argument scalar method but would also be accessible with e.g. hypergeom(z, [a], [b]), whereas 0F2 would be hypergeom(z, [], [a, b]), and 2F0 would be hypergeom(z, [a, b], []).

It's also worth noting that you can dispatch on the length of the collection by working with them as tuples, e.g.:

# ₀F₀
hypergeom(z, ::Tuple{}, ::Tuple{}) = exp(z)
# ₁F₀
hypergeom(z, a::NTuple{1}, ::Tuple{}) = (1 - z)^(-a[1])
# ₁F₁
hypergeom(z, a::NTuple{1}, b::NTuple{1}) = ...
# ...and so on...
# general case, which also works for scalars and would correctly dispatch to ₁F₁
hypergeom(z, a, b) = hypergeom(z, Tuple(a), Tuple(b))

Yes I like the tuple idea

One issue with tuple parameters is that, apart from special methods for small p & q, the same general method for pFq would be compiled for all distinct tuple sizes.

That's definitely a good point and I had considered that but I'm not sure it would be too much of an issue in practice, though I could of course be wrong! In your work, have you encountered situations where you use hypergeometric functions with many different p and q in a single session? I think that would be when compilation latency might become noticeable.

I guess the parameters don't have to be either AbstractVector or Tuple but we could support both!

FYI, I'm starting this with the internals first. Probably will tag a round with deprecation warnings (but shouldn't need to be minor release, just patch, because no exported changes). Changing AbstractVector to NTuple really is a great idea.

I settled on pFq with tuple arguments (still supporting AbstractVectors) as opposed to hypergeom. There are no parameter ambiguities with tuples, so the code leaves no user guessing whether pFq(z, a, b) is a confluent or other hypergeometric function.

Realistically, _₁F₁ and _₂F₁ have no performance difference and are provided just for scripts: in the REPL, it's much easier to type pFq anyway.