Skip to content

Commit c46f005

Browse files
committed
First cut at Hypergeometric2F1
1 parent 5cca3e5 commit c46f005

File tree

3 files changed

+131
-21
lines changed

3 files changed

+131
-21
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ New Builtins
99

1010
* ``$SessionID``
1111
* ``BinaryReadList``
12+
* ``Hypergeometric2F1``
1213

1314
Internals
1415
---------

mathics/builtin/specialfns/hypergeom.py

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import mathics.eval.tracing as tracing
1313
from mathics.core.attributes import (
1414
A_LISTABLE,
15-
A_N_HOLD_FIRST,
1615
A_NUMERIC_FUNCTION,
1716
A_PROTECTED,
1817
A_READ_PROTECTED,
@@ -21,8 +20,12 @@
2120
from mathics.core.convert.mpmath import from_mpmath
2221
from mathics.core.convert.sympy import from_sympy
2322
from mathics.core.evaluation import Evaluation
24-
from mathics.core.number import FP_MANTISA_BINARY_DIGITS
2523
from mathics.core.systemsymbols import SymbolComplexInfinity, SymbolMachinePrecision
24+
from mathics.eval.specialfns.hypergeom import (
25+
eval_Hypergeometric2F1,
26+
eval_HypergeometricPQF,
27+
eval_N_HypergeometricPQF,
28+
)
2629

2730

2831
class HypergeometricPFQ(MPMathFunction):
@@ -70,27 +73,12 @@ class HypergeometricPFQ(MPMathFunction):
7073

7174
def eval(self, a, b, z, evaluation: Evaluation):
7275
"HypergeometricPFQ[a_, b_, z_]"
73-
try:
74-
a_sympy = [e.to_sympy() for e in a]
75-
b_sympy = [e.to_sympy() for e in b]
76-
result_sympy = tracing.run_sympy(
77-
sympy.hyper, a_sympy, b_sympy, z.to_sympy()
78-
)
79-
return from_sympy(result_sympy)
80-
except Exception:
81-
pass
76+
return eval_HypergeometricPQF(a, b, z)
8277

8378
def eval_N(self, a, b, z, prec, evaluation: Evaluation):
8479
"N[HypergeometricPFQ[a_, b_, z_], prec_]"
85-
try:
86-
result_mpmath = tracing.run_mpmath(
87-
mpmath.hyper, a.to_python(), b.to_python(), z.to_python()
88-
)
89-
return from_mpmath(result_mpmath)
90-
except ZeroDivisionError:
91-
return SymbolComplexInfinity
92-
except Exception as ex:
93-
pass
80+
# FIXME: prec is not used. Why?
81+
return eval_N_HypergeometricPQF(a, b, z)
9482

9583
def eval_numeric(self, a, b, z, evaluation: Evaluation):
9684
"HypergeometricPFQ[a:{__?NumericQ}, b:{__?NumericQ}, z_?MachineNumberQ]"
@@ -124,7 +112,7 @@ class Hypergeometric1F1(MPMathFunction):
124112
"""
125113

126114
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
127-
mpmath_name = ""
115+
mpmath_name = "hymp1f1"
128116
nargs = {3}
129117
rules = {
130118
"Hypergeometric1F1[a_, b_, z_]": "HypergeometricPFQ[{a},{b},z]",
@@ -133,6 +121,45 @@ class Hypergeometric1F1(MPMathFunction):
133121
sympy_name = ""
134122

135123

124+
class Hypergeometric2F1(MPMathFunction):
125+
"""
126+
<url>
127+
:Hypergeometric function: https://en.wikipedia.org/wiki/Hypergeometric_function</url> (<url>
128+
:mpmath: https://mpmath.org/doc/current/functions/hypergeometric.html#mpmath.hyp2f1</url>, <url>
129+
:WMA: https://reference.wolfram.com/language/ref/Hypergeometric2F1.html</url>)
130+
<dl>
131+
<dt>'Hypergeometric2F1'[$a$, $b$, $c$, $z$]
132+
<dd>returns ${}_2 F_1(a; b; c; z)$.
133+
</dl>
134+
135+
>> Hypergeometric2F1[2., 3., 4., 5.0]
136+
= 0.156542 + 0.150796 I
137+
138+
Evaluate symbolically:
139+
>> Hypergeometric2F1[2, 3, 4, x]
140+
= 6 Log[1 - x] / x ^ 3 + (-6 + 3 x) / (-x ^ 2 + x ^ 3)
141+
142+
Evaluate using complex arguments:
143+
>> Hypergeometric2F1[2 + I, -I, 3/4, 0.5 - 0.5 I]
144+
= -0.972167 - 0.181659 I
145+
146+
Plot over a subset of the reals:
147+
>> Plot[Hypergeometric2F1[1/3, 1/3, 2/3, x], {x, -1, 1}]
148+
= -Graphics-
149+
"""
150+
151+
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
152+
mpmath_name = "hyp2f1"
153+
nargs = {4}
154+
summary_text = "compute Gauss hypergeometric function function"
155+
sympy_name = "hyper"
156+
157+
def eval(self, a, b, c, z, evaluation: Evaluation):
158+
"Hypergeometric2F1[a_, b_, c_, z_]"
159+
160+
return eval_Hypergeometric2F1(a, b, c, z)
161+
162+
136163
class MeijerG(MPMathFunction):
137164
"""
138165
<url>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""
2+
Evaluation functions for built-in Hypergeometric functions
3+
"""
4+
5+
from typing import Sequence, cast
6+
7+
import mpmath
8+
import sympy
9+
10+
import mathics.eval.tracing as tracing
11+
from mathics.core.atoms import Number
12+
from mathics.core.convert.mpmath import from_mpmath
13+
from mathics.core.convert.sympy import from_sympy
14+
from mathics.core.number import RECONSTRUCT_MACHINE_PRECISION_DIGITS, dps, min_prec
15+
from mathics.core.systemsymbols import SymbolComplexInfinity
16+
from mathics.eval.arithmetic import eval_mpmath_function
17+
18+
19+
def eval_Hypergeometric2F1(a, b, c, z):
20+
"""Hypergeometric2F1[a_, b_, c_, z_]
21+
22+
Uses mpmath hyp2f1 if all args are numeric, otherwise,
23+
we use sympy's hyper and expand the symbolic result.
24+
"""
25+
26+
args = (a, b, c, z)
27+
sympy_args = []
28+
all_numeric = True
29+
for arg in args:
30+
if isinstance(arg, Number):
31+
sympy_arg = arg.value
32+
else:
33+
sympy_arg = arg.to_sympy()
34+
all_numeric = False
35+
36+
sympy_args.append(sympy_arg)
37+
38+
if all_numeric:
39+
args = cast(Sequence[Number], args)
40+
41+
if any(arg.is_machine_precision() for arg in args):
42+
prec = None
43+
else:
44+
prec = min_prec(*args)
45+
if prec is None:
46+
prec = RECONSTRUCT_MACHINE_PRECISION_DIGITS
47+
d = dps(prec)
48+
args = tuple([arg.round(d) for arg in args])
49+
50+
return eval_mpmath_function(
51+
mpmath.hyp2f1, *cast(Sequence[Number], args), prec=prec
52+
)
53+
else:
54+
sympy_result = tracing.run_sympy(
55+
sympy.hyper, [sympy_args[0], sympy_args[1]], [sympy_args[2]], sympy_args[3]
56+
)
57+
return from_sympy(sympy.hyperexpand(sympy_result))
58+
59+
60+
def eval_HypergeometricPQF(a, b, z):
61+
"HypergeometricPFQ[a_, b_, z_]"
62+
try:
63+
a_sympy = [e.to_sympy() for e in a]
64+
b_sympy = [e.to_sympy() for e in b]
65+
result_sympy = tracing.run_sympy(sympy.hyper, a_sympy, b_sympy, z.to_sympy())
66+
return from_sympy(result_sympy)
67+
except Exception:
68+
return None
69+
70+
71+
# FIXME, this should take a "prec" parameter?
72+
def eval_N_HypergeometricPQF(a, b, z):
73+
"N[HypergeometricPFQ[a_, b_, z_]]"
74+
try:
75+
result_mpmath = tracing.run_mpmath(
76+
mpmath.hyper, a.to_python(), b.to_python(), z.to_python()
77+
)
78+
return from_mpmath(result_mpmath)
79+
except ZeroDivisionError:
80+
return SymbolComplexInfinity
81+
except Exception:
82+
return None

0 commit comments

Comments
 (0)