Skip to content

Commit 71081e7

Browse files
committed
Add elliptic functions..
Revise a few gamma fns.
1 parent b6a9bb2 commit 71081e7

File tree

4 files changed

+187
-39
lines changed

4 files changed

+187
-39
lines changed

CHANGES.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ Enhancements
1919
* Pyston up to versions from 2.2 to 2.3.4 are supported as are PyPy versions from 3.7-7.3.9.0 up 3.9-7.3.9. However those Python interpreters may have limitations and limitations on packages that they support.
2020
* Compatibility with the default way in which WMA sorts expressions was improved: Now expressions with fewer elements come first (issue #458).
2121

22-
22+
2323
Documentation
2424
.............
2525

2626
* "Testing Expressions" section added
2727
* "Representation of Numbers" section added
28+
* "Descriptive Statistics" section added and "Moments" folded into that
29+
* Many More URL references
30+
* Chapter and Sections are now in alphabetical order
2831

2932
New Builtins
3033
============
@@ -34,6 +37,10 @@ New Builtins
3437
* ``CompositeQ``.
3538
* ``Diagonal``. Issue #115.
3639
* ``Divisible``.
40+
* ``EllipticE``
41+
* ``EllipticF``
42+
* ``EllipticK``
43+
* ``EllipticPhi``
3744
* ``EulerPhi``
3845
* ``$Echo``. Issue #42.
3946
* ``FindRoot`` was improved for supporting numerical derivatives Issue #67, as well as the use of scipy libraries when are available.

mathics/builtin/base.py

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
from functools import total_ordering
77
import importlib
88
from itertools import chain
9-
import typing
10-
from typing import Any, Callable, Iterable, List, Optional, cast
9+
from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast
1110

1211

1312
from mathics.builtin.exceptions import (
@@ -23,6 +22,7 @@
2322
)
2423
from mathics.core.attributes import protected, read_protected, no_attributes
2524
from mathics.core.convert.expression import to_expression
25+
from mathics.core.convert.python import from_bool
2626
from mathics.core.convert.sympy import from_sympy
2727
from mathics.core.definitions import Definition
2828
from mathics.core.expression import Expression, SymbolDefault
@@ -35,8 +35,6 @@
3535
Symbol,
3636
ensure_context,
3737
strip_context,
38-
SymbolFalse,
39-
SymbolTrue,
4038
)
4139
from mathics.core.systemsymbols import SymbolHoldForm, SymbolMessageName, SymbolRule
4240

@@ -174,15 +172,15 @@ def apply_with_options(x, evaluation, options):
174172
```
175173
"""
176174

177-
name: typing.Optional[str] = None
175+
name: Optional[str] = None
178176
context: str = ""
179177
abstract: bool = False
180178
attributes: int = protected
181179
is_numeric: bool = False
182-
rules: typing.Dict[str, Any] = {}
183-
formats: typing.Dict[str, Any] = {}
184-
messages: typing.Dict[str, Any] = {}
185-
options: typing.Dict[str, Any] = {}
180+
rules: Dict[str, Any] = {}
181+
formats: Dict[str, Any] = {}
182+
messages: Dict[str, Any] = {}
183+
options: Dict[str, Any] = {}
186184
defaults = {}
187185

188186
def __new__(cls, *args, **kwargs):
@@ -409,10 +407,10 @@ def get_name(cls, short=False) -> str:
409407
return shortname
410408
return cls.context + shortname
411409

412-
def get_operator(self) -> typing.Optional[str]:
410+
def get_operator(self) -> Optional[str]:
413411
return None
414412

415-
def get_operator_display(self) -> typing.Optional[str]:
413+
def get_operator_display(self) -> Optional[str]:
416414
return None
417415

418416
def get_functions(self, prefix="apply", is_pymodule=False):
@@ -535,17 +533,17 @@ def get_name(self, short=False) -> str:
535533

536534

537535
class Operator(Builtin):
538-
operator: typing.Optional[str] = None
539-
precedence: typing.Optional[int] = None
536+
operator: Optional[str] = None
537+
precedence: Optional[int] = None
540538
precedence_parse = None
541539
needs_verbatim = False
542540

543541
default_formats = True
544542

545-
def get_operator(self) -> typing.Optional[str]:
543+
def get_operator(self) -> Optional[str]:
546544
return self.operator
547545

548-
def get_operator_display(self) -> typing.Optional[str]:
546+
def get_operator_display(self) -> Optional[str]:
549547
if hasattr(self, "operator_display"):
550548
return self.operator_display
551549
else:
@@ -561,7 +559,7 @@ def get_functions(self, prefix="apply", is_pymodule=False) -> List[Callable]:
561559

562560

563561
class SympyObject(Builtin):
564-
sympy_name: typing.Optional[str] = None
562+
sympy_name: Optional[str] = None
565563

566564
mathics_to_sympy = {}
567565

@@ -574,7 +572,7 @@ def __init__(self, *args, **kwargs):
574572
def is_constant(self) -> bool:
575573
return False
576574

577-
def get_sympy_names(self) -> typing.List[str]:
575+
def get_sympy_names(self) -> List[str]:
578576
if self.sympy_name:
579577
return [self.sympy_name]
580578
return []
@@ -657,15 +655,10 @@ def __init__(self, *args, **kwargs):
657655

658656

659657
class Test(Builtin):
660-
def apply(self, expr, evaluation) -> Symbol:
658+
def apply(self, expr, evaluation) -> Optional[Symbol]:
661659
"%(name)s[expr_]"
662-
tst = self.test(expr)
663-
if tst:
664-
return SymbolTrue
665-
elif tst is False:
666-
return SymbolFalse
667-
else:
668-
return
660+
test_expr = self.test(expr)
661+
return None if test_expr is None else from_bool(bool(test_expr))
669662

670663

671664
class SympyFunction(SympyObject):
@@ -905,7 +898,7 @@ def __init__(self, name, count, expected):
905898
class PatternObject(BuiltinElement, Pattern):
906899
needs_verbatim = True
907900

908-
arg_counts: typing.List[int] = []
901+
arg_counts: List[int] = []
909902

910903
def init(self, expr):
911904
super().init(expr)
@@ -968,7 +961,7 @@ class CountableInteger:
968961
# _support_infinity to False.
969962
_finite: bool
970963
_upper_limit: bool
971-
_integer: typing.Union[str, int]
964+
_integer: Union[str, int]
972965
_support_infinity = False
973966

974967
def __init__(self, value="Infinity", upper_limit=True):
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
"""
2+
Elliptic Integrals
3+
4+
In integral calculus, an elliptic integral is one of a number of related functions defined as the value of certain integral. Their name originates from their originally arising in connection with the problem of finding the arc length of an ellipse. These funcion often are used in cryptography to encode and decode messages.
5+
6+
See <url>https://en.wikipedia.org/wiki/Elliptic_integral</url>.
7+
"""
8+
9+
from mathics.core.attributes import (
10+
listable as A_LISTABLE,
11+
numeric_function as A_NUMERIC_FUNCTION,
12+
protected as A_PROTECTED,
13+
)
14+
from mathics.builtin.base import SympyFunction
15+
from mathics.core.convert.sympy import from_sympy
16+
17+
import sympy
18+
19+
20+
class EllipticE(SympyFunction):
21+
"""
22+
<dl>
23+
<dt>'EllipticE[$m$]'
24+
<dd>computes the complete elliptic integral $E$($m$).
25+
26+
<dt>'EllipticE[ϕ|$m$]'
27+
<dd>computes the complete elliptic integral of the second kind $E$($m$|$ϕ$).
28+
</dl>
29+
30+
Elliptic curves give Pi / 2 when evaluated at zero:
31+
>> EllipticE[0]
32+
= Pi / 2
33+
34+
>> EllipticE[0.3, 0.8]
35+
= 0.296426
36+
37+
Plot over a reals centered around 0:
38+
>> Plot[EllipticE[m], {m, -2, 2}]
39+
= -Graphics-
40+
"""
41+
42+
attributes = A_NUMERIC_FUNCTION | A_PROTECTED
43+
summary_text = "elliptic integral of the second kind E(ϕ|m)"
44+
sympy_name = "elliptic_e"
45+
46+
def apply_m(self, m, evaluation):
47+
"%(name)s[m_]"
48+
sympy_arg = m.numerify(evaluation).to_sympy()
49+
return from_sympy(sympy.elliptic_e(sympy_arg))
50+
51+
def apply_phi_m(self, phi, m, evaluation):
52+
"%(name)s[phi_, m_]"
53+
sympy_args = [a.numerify(evaluation).to_sympy() for a in (phi, m)]
54+
return from_sympy(sympy.elliptic_e(*sympy_args))
55+
56+
57+
class EllipticF(SympyFunction):
58+
"""
59+
<dl>
60+
<dt>'EllipticF[$m$]'
61+
<dd>computes the elliptic integral of the first kind $F$($ϕ$|$m$).
62+
</dl>
63+
64+
EllipticF is zero when the firt argument is zero:
65+
>> EllipticF[0.3, 0.8]
66+
= 0.303652
67+
68+
>> EllipticF[0, 0.8]
69+
= 0
70+
71+
"""
72+
73+
attributes = A_NUMERIC_FUNCTION | A_PROTECTED
74+
summary_text = "elliptic integral F(ϕ|m)"
75+
sympy_name = "elliptic_f"
76+
77+
def apply(self, phi, m, evaluation):
78+
"%(name)s[phi_, m_]"
79+
sympy_args = [a.numerify(evaluation).to_sympy() for a in (phi, m)]
80+
return from_sympy(sympy.elliptic_f(*sympy_args))
81+
82+
83+
class EllipticK(SympyFunction):
84+
"""
85+
<dl>
86+
<dt>'EllipticK[$m$]'
87+
<dd>computes the elliptic integral of the first kind $K$($m$).
88+
</dl>
89+
90+
>> EllipticK[0.5]
91+
= 1.85407
92+
93+
Elliptic curves give Pi / 2 when evaluated at zero:
94+
>> EllipticK[0]
95+
= Pi / 2
96+
97+
Plot over a reals around 0:
98+
>> Plot[EllipticK[n], {n, -1, 1}]
99+
= -Graphics-
100+
"""
101+
102+
attributes = A_NUMERIC_FUNCTION | A_LISTABLE | A_PROTECTED
103+
summary_text = "elliptic integral of the first kind K(m)"
104+
sympy_name = "elliptic_k"
105+
106+
def apply(self, m, evaluation):
107+
"%(name)s[m__]"
108+
args = m.numerify(evaluation).get_sequence()
109+
sympy_args = [a.to_sympy() for a in args]
110+
return from_sympy(sympy.elliptic_k(*sympy_args))
111+
112+
113+
class EllipticPi(SympyFunction):
114+
"""
115+
<dl>
116+
<dt>'EllipticPi[$n$, $m$]'
117+
<dd>computes the elliptic integral of the third kind $Pi$($m$).
118+
</dl>
119+
120+
>> EllipticPi[0.4, 0.6]
121+
= 2.89281
122+
123+
Elliptic curves give Pi / 2 when evaluated at zero:
124+
>> EllipticPi[0, 0]
125+
= Pi / 2
126+
127+
"""
128+
129+
attributes = A_NUMERIC_FUNCTION | A_PROTECTED
130+
summary_text = "elliptic integral of the third kind P(n|m)"
131+
sympy_name = "elliptic_pi"
132+
133+
def apply_n_m(self, n, m, evaluation):
134+
"%(name)s[n_, m_]"
135+
sympy_n = m.numerify(evaluation).to_sympy()
136+
sympy_m = n.numerify(evaluation).to_sympy()
137+
return from_sympy(sympy.elliptic_pi(sympy_n, sympy_m))
138+
139+
def apply_n_phi_m(self, n, phi, m, evaluation):
140+
"%(name)s[n_, phi_, m_]"
141+
sympy_n = m.numerify(evaluation).to_sympy()
142+
sympy_phi = m.numerify(evaluation).to_sympy()
143+
sympy_m = n.numerify(evaluation).to_sympy()
144+
return from_sympy(sympy.elliptic_pi(sympy_n, sympy_phi, sympy_m))

mathics/builtin/specialfns/gamma.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
Integer1,
1818
Number,
1919
)
20-
from mathics.core.attributes import listable, numeric_function, protected
20+
from mathics.core.attributes import (
21+
listable as A_LISTABLE,
22+
numeric_function as A_NUMERIC_FUNCTION,
23+
protected as A_PROTECTED,
24+
)
2125
from mathics.core.convert.mpmath import from_mpmath
2226
from mathics.core.convert.python import from_python
2327
from mathics.core.convert.sympy import from_sympy
@@ -51,7 +55,7 @@ class Beta(_MPMathMultiFunction):
5155
"""
5256

5357
summary_text = "Euler's Beta function"
54-
attributes = listable | numeric_function | protected
58+
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
5559
mpmath_names = {
5660
2: "beta", # two arguments
5761
3: "betainc", # three arguments
@@ -164,12 +168,12 @@ class Factorial(PostfixOperator, _MPMathFunction):
164168
= 1
165169
"""
166170

167-
summary_text = "factorial"
168-
attributes = numeric_function | protected
171+
attributes = A_NUMERIC_FUNCTION | A_PROTECTED
169172

173+
mpmath_name = "factorial"
170174
operator = "!"
171175
precedence = 610
172-
mpmath_name = "factorial"
176+
summary_text = "factorial"
173177

174178

175179
class Factorial2(PostfixOperator, _MPMathFunction):
@@ -196,7 +200,7 @@ class Factorial2(PostfixOperator, _MPMathFunction):
196200
= 3.35237
197201
"""
198202

199-
attributes = numeric_function | protected
203+
attributes = A_NUMERIC_FUNCTION | A_PROTECTED
200204
operator = "!!"
201205
precedence = 610
202206
mpmath_name = "fac2"
@@ -394,15 +398,15 @@ class Pochhammer(SympyFunction):
394398
= 6652800
395399
"""
396400

397-
attributes = listable | numeric_function | protected
401+
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
398402

399-
sympy_name = "RisingFactorial"
400-
summary_text = "Pochhammer's symbols"
401403
rules = {
402404
"Pochhammer[a_, n_]": "Gamma[a + n] / Gamma[a]",
403405
"Derivative[1,0][Pochhammer]": "(Pochhammer[#1, #2]*(-PolyGamma[0, #1] + PolyGamma[0, #1 + #2]))&",
404406
"Derivative[0,1][Pochhammer]": "(Pochhammer[#1, #2]*PolyGamma[0, #1 + #2])&",
405407
}
408+
summary_text = "Pochhammer's symbols"
409+
sympy_name = "RisingFactorial"
406410

407411

408412
class PolyGamma(_MPMathMultiFunction):
@@ -422,7 +426,7 @@ class PolyGamma(_MPMathMultiFunction):
422426
>> PolyGamma[3, 5]
423427
= -22369 / 3456 + Pi ^ 4 / 15
424428
"""
425-
attributes = listable | numeric_function | protected
429+
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
426430

427431
mpmath_names = {
428432
1: "digamma", # 1 argument
@@ -450,7 +454,7 @@ class StieltjesGamma(SympyFunction):
450454
## = ...
451455
"""
452456

453-
attributes = listable | numeric_function | protected
457+
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
454458

455459
summary_text = "Stieltjes' function"
456460
sympy_name = "stieltjes"

0 commit comments

Comments
 (0)