Skip to content

Commit

Permalink
Add elliptic functions..
Browse files Browse the repository at this point in the history
Revise a few gamma fns.
  • Loading branch information
rocky committed Jul 27, 2022
1 parent b6a9bb2 commit 93ed59e
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 39 deletions.
9 changes: 8 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ Enhancements
* 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.
* Compatibility with the default way in which WMA sorts expressions was improved: Now expressions with fewer elements come first (issue #458).


Documentation
.............

* "Testing Expressions" section added
* "Representation of Numbers" section added
* "Descriptive Statistics" section added and "Moments" folded into that
* Many More URL references
* Chapter and Sections are now in alphabetical order

New Builtins
============
Expand All @@ -34,6 +37,10 @@ New Builtins
* ``CompositeQ``.
* ``Diagonal``. Issue #115.
* ``Divisible``.
* ``EllipticE``
* ``EllipticF``
* ``EllipticK``
* ``EllipticPhi``

This comment has been minimized.

Copy link
@mmatera

mmatera Jul 27, 2022

Contributor

EllipticPi

This comment has been minimized.

Copy link
@rocky

rocky Jul 27, 2022

Author Member

Should be fixed in a9a0190

* ``EulerPhi``
* ``$Echo``. Issue #42.
* ``FindRoot`` was improved for supporting numerical derivatives Issue #67, as well as the use of scipy libraries when are available.
Expand Down
47 changes: 20 additions & 27 deletions mathics/builtin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
from functools import total_ordering
import importlib
from itertools import chain
import typing
from typing import Any, Callable, Iterable, List, Optional, cast
from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast


from mathics.builtin.exceptions import (
Expand All @@ -23,6 +22,7 @@
)
from mathics.core.attributes import protected, read_protected, no_attributes
from mathics.core.convert.expression import to_expression
from mathics.core.convert.python import from_bool
from mathics.core.convert.sympy import from_sympy
from mathics.core.definitions import Definition
from mathics.core.expression import Expression, SymbolDefault
Expand All @@ -35,8 +35,6 @@
Symbol,
ensure_context,
strip_context,
SymbolFalse,
SymbolTrue,
)
from mathics.core.systemsymbols import SymbolHoldForm, SymbolMessageName, SymbolRule

Expand Down Expand Up @@ -174,15 +172,15 @@ def apply_with_options(x, evaluation, options):
```
"""

name: typing.Optional[str] = None
name: Optional[str] = None
context: str = ""
abstract: bool = False
attributes: int = protected
is_numeric: bool = False
rules: typing.Dict[str, Any] = {}
formats: typing.Dict[str, Any] = {}
messages: typing.Dict[str, Any] = {}
options: typing.Dict[str, Any] = {}
rules: Dict[str, Any] = {}
formats: Dict[str, Any] = {}
messages: Dict[str, Any] = {}
options: Dict[str, Any] = {}
defaults = {}

def __new__(cls, *args, **kwargs):
Expand Down Expand Up @@ -409,10 +407,10 @@ def get_name(cls, short=False) -> str:
return shortname
return cls.context + shortname

def get_operator(self) -> typing.Optional[str]:
def get_operator(self) -> Optional[str]:
return None

def get_operator_display(self) -> typing.Optional[str]:
def get_operator_display(self) -> Optional[str]:
return None

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


class Operator(Builtin):
operator: typing.Optional[str] = None
precedence: typing.Optional[int] = None
operator: Optional[str] = None
precedence: Optional[int] = None
precedence_parse = None
needs_verbatim = False

default_formats = True

def get_operator(self) -> typing.Optional[str]:
def get_operator(self) -> Optional[str]:
return self.operator

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


class SympyObject(Builtin):
sympy_name: typing.Optional[str] = None
sympy_name: Optional[str] = None

mathics_to_sympy = {}

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

def get_sympy_names(self) -> typing.List[str]:
def get_sympy_names(self) -> List[str]:
if self.sympy_name:
return [self.sympy_name]
return []
Expand Down Expand Up @@ -657,15 +655,10 @@ def __init__(self, *args, **kwargs):


class Test(Builtin):
def apply(self, expr, evaluation) -> Symbol:
def apply(self, expr, evaluation) -> Optional[Symbol]:
"%(name)s[expr_]"
tst = self.test(expr)
if tst:
return SymbolTrue
elif tst is False:
return SymbolFalse
else:
return
test_expr = self.test(expr)
return None if test_expr is None else from_bool(bool(test_expr))


class SympyFunction(SympyObject):
Expand Down Expand Up @@ -905,7 +898,7 @@ def __init__(self, name, count, expected):
class PatternObject(BuiltinElement, Pattern):
needs_verbatim = True

arg_counts: typing.List[int] = []
arg_counts: List[int] = []

def init(self, expr):
super().init(expr)
Expand Down Expand Up @@ -968,7 +961,7 @@ class CountableInteger:
# _support_infinity to False.
_finite: bool
_upper_limit: bool
_integer: typing.Union[str, int]
_integer: Union[str, int]
_support_infinity = False

def __init__(self, value="Infinity", upper_limit=True):
Expand Down
144 changes: 144 additions & 0 deletions mathics/builtin/specialfns/elliptic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
"""
Elliptic Integrals
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.
See <url>https://en.wikipedia.org/wiki/Elliptic_integral</url>.
"""

from mathics.core.attributes import (
listable as A_LISTABLE,
numeric_function as A_NUMERIC_FUNCTION,
protected as A_PROTECTED,
)
from mathics.builtin.base import SympyFunction
from mathics.core.convert.sympy import from_sympy

import sympy


class EllipticE(SympyFunction):

This comment has been minimized.

Copy link
@mmatera

mmatera Jul 27, 2022

Contributor
  • Attributes are OK for all these builtins.
  • EllipticK takes just 1 argument
  • EllipticE takes 1 or 2 arguments
  • EllipticF takes just 2 arguments
  • EllipticPi takes 2 or 3 arguments.

in WMA, if you pass a wrong number of arguments, a message like this is raised:

EllipticF::argrx: EllipticF called with 3 arguments; 2 arguments are expected.
EllipticE::argt: 
   EllipticE called with 3 arguments; 1 or 2 arguments are expected.

This comment has been minimized.

Copy link
@rocky

rocky Jul 27, 2022

Author Member

Thanks - and if the wrong types on the arguments?

This comment has been minimized.

Copy link
@mmatera

mmatera Jul 27, 2022

Contributor

In that case, the expression is kept unevaluated.

This comment has been minimized.

Copy link
@mmatera

mmatera Jul 27, 2022

Contributor
In[1]:= EllipticK["s"]                                                          

Out[1]= EllipticK[s]
"""
<dl>
<dt>'EllipticE[$m$]'
<dd>computes the complete elliptic integral $E$($m$).
<dt>'EllipticE[ϕ|$m$]'
<dd>computes the complete elliptic integral of the second kind $E$($m$|$ϕ$).
</dl>
Elliptic curves give Pi / 2 when evaluated at zero:
>> EllipticE[0]
= Pi / 2
>> EllipticE[0.3, 0.8]
= 0.296426
Plot over a reals centered around 0:
>> Plot[EllipticE[m], {m, -2, 2}]
= -Graphics-
"""

attributes = A_NUMERIC_FUNCTION | A_PROTECTED
summary_text = "elliptic integral of the second kind E(ϕ|m)"
sympy_name = "elliptic_e"

def apply_m(self, m, evaluation):
"%(name)s[m_]"
sympy_arg = m.numerify(evaluation).to_sympy()
return from_sympy(sympy.elliptic_e(sympy_arg))

def apply_phi_m(self, phi, m, evaluation):
"%(name)s[phi_, m_]"
sympy_args = [a.numerify(evaluation).to_sympy() for a in (phi, m)]
return from_sympy(sympy.elliptic_e(*sympy_args))


class EllipticF(SympyFunction):
"""
<dl>
<dt>'EllipticF[$m$]'
<dd>computes the elliptic integral of the first kind $F$($ϕ$|$m$).
</dl>
>> EllipticF[0.3, 0.8]
= 0.303652
EllipticF is zero when the firt argument is zero:
>> EllipticF[0, 0.8]
= 0
"""

attributes = A_NUMERIC_FUNCTION | A_PROTECTED
summary_text = "elliptic integral F(ϕ|m)"
sympy_name = "elliptic_f"

def apply(self, phi, m, evaluation):
"%(name)s[phi_, m_]"
sympy_args = [a.numerify(evaluation).to_sympy() for a in (phi, m)]
return from_sympy(sympy.elliptic_f(*sympy_args))


class EllipticK(SympyFunction):
"""
<dl>
<dt>'EllipticK[$m$]'
<dd>computes the elliptic integral of the first kind $K$($m$).
</dl>
>> EllipticK[0.5]
= 1.85407
Elliptic curves give Pi / 2 when evaluated at zero:
>> EllipticK[0]
= Pi / 2
Plot over a reals around 0:
>> Plot[EllipticK[n], {n, -1, 1}]
= -Graphics-
"""

attributes = A_NUMERIC_FUNCTION | A_LISTABLE | A_PROTECTED
summary_text = "elliptic integral of the first kind K(m)"
sympy_name = "elliptic_k"

def apply(self, m, evaluation):
"%(name)s[m__]"
args = m.numerify(evaluation).get_sequence()
sympy_args = [a.to_sympy() for a in args]
return from_sympy(sympy.elliptic_k(*sympy_args))


class EllipticPi(SympyFunction):
"""
<dl>
<dt>'EllipticPi[$n$, $m$]'
<dd>computes the elliptic integral of the third kind $Pi$($m$).
</dl>
>> EllipticPi[0.4, 0.6]
= 2.89281
Elliptic curves give Pi / 2 when evaluated at zero:
>> EllipticPi[0, 0]
= Pi / 2
"""

attributes = A_NUMERIC_FUNCTION | A_PROTECTED
summary_text = "elliptic integral of the third kind P(n|m)"
sympy_name = "elliptic_pi"

def apply_n_m(self, n, m, evaluation):
"%(name)s[n_, m_]"
sympy_n = m.numerify(evaluation).to_sympy()
sympy_m = n.numerify(evaluation).to_sympy()
return from_sympy(sympy.elliptic_pi(sympy_n, sympy_m))

def apply_n_phi_m(self, n, phi, m, evaluation):
"%(name)s[n_, phi_, m_]"
sympy_n = m.numerify(evaluation).to_sympy()
sympy_phi = m.numerify(evaluation).to_sympy()
sympy_m = n.numerify(evaluation).to_sympy()
return from_sympy(sympy.elliptic_pi(sympy_n, sympy_phi, sympy_m))
26 changes: 15 additions & 11 deletions mathics/builtin/specialfns/gamma.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
Integer1,
Number,
)
from mathics.core.attributes import listable, numeric_function, protected
from mathics.core.attributes import (
listable as A_LISTABLE,
numeric_function as A_NUMERIC_FUNCTION,
protected as A_PROTECTED,
)
from mathics.core.convert.mpmath import from_mpmath
from mathics.core.convert.python import from_python
from mathics.core.convert.sympy import from_sympy
Expand Down Expand Up @@ -51,7 +55,7 @@ class Beta(_MPMathMultiFunction):
"""

summary_text = "Euler's Beta function"
attributes = listable | numeric_function | protected
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
mpmath_names = {
2: "beta", # two arguments
3: "betainc", # three arguments
Expand Down Expand Up @@ -164,12 +168,12 @@ class Factorial(PostfixOperator, _MPMathFunction):
= 1
"""

summary_text = "factorial"
attributes = numeric_function | protected
attributes = A_NUMERIC_FUNCTION | A_PROTECTED

mpmath_name = "factorial"
operator = "!"
precedence = 610
mpmath_name = "factorial"
summary_text = "factorial"


class Factorial2(PostfixOperator, _MPMathFunction):
Expand All @@ -196,7 +200,7 @@ class Factorial2(PostfixOperator, _MPMathFunction):
= 3.35237
"""

attributes = numeric_function | protected
attributes = A_NUMERIC_FUNCTION | A_PROTECTED
operator = "!!"
precedence = 610
mpmath_name = "fac2"
Expand Down Expand Up @@ -394,15 +398,15 @@ class Pochhammer(SympyFunction):
= 6652800
"""

attributes = listable | numeric_function | protected
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED

sympy_name = "RisingFactorial"
summary_text = "Pochhammer's symbols"
rules = {
"Pochhammer[a_, n_]": "Gamma[a + n] / Gamma[a]",
"Derivative[1,0][Pochhammer]": "(Pochhammer[#1, #2]*(-PolyGamma[0, #1] + PolyGamma[0, #1 + #2]))&",
"Derivative[0,1][Pochhammer]": "(Pochhammer[#1, #2]*PolyGamma[0, #1 + #2])&",
}
summary_text = "Pochhammer's symbols"
sympy_name = "RisingFactorial"


class PolyGamma(_MPMathMultiFunction):
Expand All @@ -422,7 +426,7 @@ class PolyGamma(_MPMathMultiFunction):
>> PolyGamma[3, 5]
= -22369 / 3456 + Pi ^ 4 / 15
"""
attributes = listable | numeric_function | protected
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED

mpmath_names = {
1: "digamma", # 1 argument
Expand Down Expand Up @@ -450,7 +454,7 @@ class StieltjesGamma(SympyFunction):
## = ...
"""

attributes = listable | numeric_function | protected
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED

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

2 comments on commit 93ed59e

@rocky
Copy link
Member Author

@rocky rocky commented on 93ed59e Jul 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops - had meant to do these in a branch.

@rocky
Copy link
Member Author

@rocky rocky commented on 93ed59e Jul 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mmatera I'd be grateful if you would check ElllpticE, ElllipticF, EllipticK, and EllipticPi for Attribuets setitings.

Again, I am sorry that i didn't put this in a PR in a branch.

Please sign in to comment.