Skip to content

Commit fa42db0

Browse files
committed
Merge branch 'master' into makeboxesoverhault
2 parents c320553 + b73434c commit fa42db0

16 files changed

Lines changed: 222 additions & 7340 deletions

File tree

.github/workflows/windows.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Mathics (Windows)
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
build:
11+
runs-on: windows-latest
12+
strategy:
13+
matrix:
14+
os: [windows]
15+
python-version: [3.6, 3.7, 3.8, 3.9]
16+
steps:
17+
- uses: actions/checkout@v2
18+
- name: Set up Python ${{ matrix.python-version }}
19+
uses: actions/setup-python@v2
20+
with:
21+
python-version: ${{ matrix.python-version }}
22+
- name: Install dependencies
23+
run: |
24+
choco llvm
25+
python -m pip install --upgrade pip
26+
LLVM_CONFIG=/usr/local/Cellar/llvm@9/9.0.1_2/bin/llvm-config pip install llvmlite
27+
- name: Install Mathics
28+
run: |
29+
make develop
30+
- name: Test Mathics
31+
run: |
32+
pip install pytest pexpect
33+
make -j3 check

mathics/builtin/arithmetic.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import sympy
1313
import mpmath
14-
import math
1514

1615
from mathics.builtin.base import (
1716
Builtin,
@@ -1343,14 +1342,13 @@ class PossibleZeroQ(SympyFunction):
13431342
def apply(self, expr, evaluation):
13441343
"%(name)s[expr_]"
13451344
from sympy.matrices.utilities import _iszero
1346-
13471345
sympy_expr = expr.to_sympy()
13481346
result = _iszero(sympy_expr)
13491347
if result is None:
13501348
# try expanding the expression
13511349
exprexp = Expression("ExpandAll", expr).evaluate(evaluation)
13521350
exprexp = exprexp.to_sympy()
1353-
result = _iszero(exprexp)
1351+
result = _iszero(exprexp)
13541352
if result is None:
13551353
# Can't get exact answer, so try approximate equal
13561354
numeric_val = Expression("N", expr).evaluate(evaluation)
@@ -1364,7 +1362,6 @@ def apply(self, expr, evaluation):
13641362
if Expression("Simplify", expr).evaluate(evaluation) == Integer(0)
13651363
else SymbolFalse
13661364
)
1367-
13681365
return from_python(result)
13691366

13701367

mathics/builtin/base.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33

4-
import mpmath
54
import re
65
import sympy
76

87
from functools import total_ordering
98
import importlib
109
from itertools import chain
11-
1210
import typing
1311
from typing import Any, cast
1412

@@ -533,7 +531,7 @@ def apply(self, expr, evaluation) -> Symbol:
533531

534532

535533
class SympyFunction(SympyObject):
536-
def get_constant(self, precision, have_mpmath=False):
534+
def get_constant(self, precision, evaluation, have_mpmath=False):
537535
try:
538536
d = get_precision(precision, evaluation)
539537
except PrecisionValueError:

mathics/builtin/constants.py

Lines changed: 107 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
Symbol,
2020
strip_context,
2121
)
22-
from mathics.core.numbers import get_precision, PrecisionValueError
22+
from mathics.core.numbers import get_precision, PrecisionValueError, machine_precision
2323

2424

2525
def mp_constant(fn: str, d=None) -> mpmath.ctx_mp_python.mpf:
@@ -29,7 +29,11 @@ def mp_constant(fn: str, d=None) -> mpmath.ctx_mp_python.mpf:
2929
if d is None:
3030
return getattr(mpmath, fn)()
3131
else:
32-
mpmath.mp.dps = int_d = int(d)
32+
# TODO: In some functions like Pi, you can
33+
# ask for a certain number of digits, but the
34+
# accuracy will be less than that. Figure out
35+
# what's up and compensate somehow.
36+
mpmath.mp.dps = int_d = int(d * 3.321928)
3337
return getattr(mpmath, fn)(prec=int_d)
3438

3539

@@ -43,7 +47,16 @@ def mp_convert_constant(obj, **kwargs):
4347

4448

4549
def numpy_constant(name: str, d=None) -> float:
46-
return getattr(numpy, name)
50+
if d:
51+
# by mmatera: Here I have a question:
52+
# 0.0123`2 should be rounded to
53+
# 0.01 or to 0.0123?
54+
# (absolute versus relative accuracy)
55+
val = getattr(numpy, name)
56+
val = numpy.round(val, d)
57+
return val
58+
else:
59+
return getattr(numpy, name)
4760

4861

4962
def sympy_constant(fn, d=None):
@@ -54,46 +67,75 @@ class _Constant_Common(Predefined):
5467

5568
attributes = ("Constant", "Protected", "ReadProtected")
5669
nargs = 0
70+
options = {"Method": "Automatic"}
5771

58-
def apply_N(self, precision, evaluation):
59-
"N[%(name)s, precision_]"
60-
return self.get_constant(precision, evaluation)
72+
def apply_N(self, precision, evaluation, options={}):
73+
"N[%(name)s, precision_?NumericQ, OptionsPattern[%(name)s]]"
74+
75+
preference = self.get_option(options, "Method", evaluation).get_string_value()
76+
if preference == "Automatic":
77+
return self.get_constant(precision, evaluation)
78+
else:
79+
return self.get_constant(precision, evaluation, preference)
80+
81+
def apply_N2(self, evaluation, options={}):
82+
"N[%(name)s, OptionsPattern[%(name)s]]"
83+
return self.apply_N(None, evaluation, options)
6184

6285
def is_constant(self) -> bool:
6386
return True
6487

6588
def get_constant(self, precision, evaluation, preference=None):
66-
## print("XXX", self, preference)
67-
if preference is None:
68-
preference = (
69-
evaluation.parse("Settings`$PreferredBackendMethod")
70-
.evaluate(evaluation)
71-
.get_string_value()
72-
)
73-
# TODO: validate PreferredBackendMethod is in "mpmath", "numpy", "sympy"
74-
try:
75-
d = get_precision(precision, evaluation)
76-
except PrecisionValueError:
77-
d = None
89+
# first, determine the precision
90+
machine_d = int( 0.30103 * machine_precision)
91+
d = None
92+
if precision:
93+
try:
94+
d = get_precision(precision, evaluation)
95+
except PrecisionValueError:
96+
pass
7897

79-
conversion_fn = MachineReal if d is None else PrecisionReal
80-
81-
# print("XXX1", self, preference, conversion_fn)
98+
if d is None:
99+
d = machine_d
82100

83-
if preference == "sympy" and hasattr(self, "sympy_name"):
84-
value = sympy_constant(self.sympy_name, d)
85-
elif preference == "mpmath" and hasattr(self, "mpmath_name"):
86-
value = mp_constant(self.mpmath_name, d)
87-
elif preference == "numpy" and hasattr(self, "numpy_name"):
88-
value = numpy_constant(self.numpy_name)
89-
elif hasattr(self, "mpmath_name"):
90-
value = mp_constant(self.mpmath_name, d)
91-
elif hasattr(self, "sympy_name"):
92-
value = sympy_constant(self.sympy_name, d)
93-
elif hasattr(self, "numpy_name"):
101+
# If preference not especified, determine it
102+
# from the precision.
103+
if preference is None:
104+
if d <= machine_d:
105+
preference = "numpy"
106+
else:
107+
preference = "mpmath"
108+
# If preference is not valid, send a message and return.
109+
if not (preference in ("sympy", "numpy", "mpmath")):
110+
evaluation.message(f'{preference} not in ("sympy", "numpy", "mpmath")')
111+
return
112+
# Try to determine the numeric value
113+
value = None
114+
if preference == "mpmath" and not hasattr(self, "mpmath_name"):
115+
preference = "numpy"
116+
elif preference == "sympy" and not hasattr(self, "sympy_name"):
117+
preference = "numpy"
118+
119+
if preference == "numpy" and not hasattr(self, "numpy_name"):
120+
if hasattr(self, "sympy_name"):
121+
preference = "sympy"
122+
elif hasattr(self, "mpmath_name"):
123+
preference = "mpmath"
124+
else:
125+
preference = ""
126+
if preference == "numpy":
94127
value = numpy_constant(self.numpy_name)
95-
return conversion_fn(value)
96-
128+
if d == machine_d:
129+
return MachineReal(value)
130+
if preference == "sympy":
131+
value = sympy_constant(self.sympy_name, d+2)
132+
if preference == "mpmath":
133+
value = mp_constant(self.mpmath_name, d*2)
134+
if value:
135+
return PrecisionReal(sympy.Float(str(value), d))
136+
# If the value is not available, return none
137+
# and keep it unevaluated.
138+
return
97139

98140
class MPMathConstant(_Constant_Common):
99141
"""Representation of a constant in mpmath, e.g. Pi, E, I, etc."""
@@ -152,22 +194,22 @@ def to_sympy(self, expr=None, **kwargs):
152194
return None
153195

154196

155-
class Catalan(MPMathConstant, NumpyConstant, SympyConstant):
197+
class Catalan(MPMathConstant, SympyConstant):
156198
"""
157199
<dl>
158200
<dt>'Catalan'
159201
<dd>is Catalan's constant with numerical value \u2243 0.915966.
160202
</dl>
161203
162204
>> Catalan // N
163-
= 0.915966
205+
= 0.915965594177219
164206
165207
>> N[Catalan, 20]
166208
= 0.91596559417721901505
167209
"""
168210

169211
mpmath_name = "catalan"
170-
numpy_name = "catalan"
212+
# numpy_name = "catalan" ## This is not defined in numpy
171213
sympy_name = "Catalan"
172214

173215

@@ -239,10 +281,13 @@ def to_numpy(self, expr=None, **kwargs):
239281
# return mpmath.degree
240282
return numpy.pi / 180
241283

242-
def apply_N(self, precision, evaluation):
243-
"N[Degree, precision_]"
284+
def apply_N(self, precision, evaluation, options={}):
285+
"N[Degree, precision_, OptionsPattern[%(name)s]]"
244286
try:
245-
d = get_precision(precision, evaluation)
287+
if precision:
288+
d = get_precision(precision, evaluation)
289+
else:
290+
d = get_precision(Symbol("System`MachinePrecision"), evaluation)
246291
except PrecisionValueError:
247292
return
248293

@@ -278,8 +323,8 @@ class E(MPMathConstant, NumpyConstant, SympyConstant):
278323
numpy_name = "e"
279324
sympy_name = "E"
280325

281-
def apply_N(self, precision, evaluation):
282-
"N[E, precision_]"
326+
def apply_N(self, precision, evaluation, options={}):
327+
"N[E, precision_, OptionsPattern[%(name)s]]"
283328
return self.get_constant(precision, evaluation)
284329

285330

@@ -310,9 +355,10 @@ class Glaisher(MPMathConstant):
310355
</dl>
311356
312357
>> N[Glaisher]
313-
= 1.28243
358+
= 1.28242712910062
314359
>> N[Glaisher, 50]
315-
= 1.2824271291006219541941391071304678916931152343750
360+
= 1.2824271291006226368753425688697917277676889273250
361+
# 1.2824271291006219541941391071304678916931152343750
316362
"""
317363

318364
mpmath_name = "glaisher"
@@ -326,7 +372,7 @@ class GoldenRatio(MPMathConstant, SympyConstant):
326372
</dl>
327373
328374
>> GoldenRatio // N
329-
= 1.61803
375+
= 1.61803398874989
330376
>> N[GoldenRatio, 40]
331377
= 1.618033988749894848204586834365638117720
332378
"""
@@ -399,9 +445,10 @@ class Khinchin(MPMathConstant):
399445
</dl>
400446
401447
>> N[Khinchin]
402-
= 2.68545
448+
= 2.68545200106531
403449
>> N[Khinchin, 50]
404-
= 2.6854520010653075701156922150403261184692382812500
450+
= 2.6854520010653064453097148354817956938203822939945
451+
# = 2.6854520010653075701156922150403261184692382812500
405452
"""
406453

407454
mpmath_name = "khinchin"
@@ -416,7 +463,19 @@ class Pi(MPMathConstant, SympyConstant):
416463
417464
>> N[Pi]
418465
= 3.14159
419-
>> N[Pi, 50]
466+
467+
Force using the value given from numpy to compute Pi.
468+
>> N[Pi, Method->"numpy"]
469+
= 3.14159
470+
471+
Force using the value given from sympy to compute Pi to 3 places,
472+
two places after the decimal point.
473+
474+
Note that sympy is the default method.
475+
>> N[Pi, 3, Method->"sympy"]
476+
= 3.14
477+
478+
>> N[Pi, 50]
420479
= 3.1415926535897932384626433832795028841971693993751
421480
>> Attributes[Pi]
422481
= {Constant, Protected, ReadProtected}

mathics/builtin/datentime.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ class TimeConstrained(Builtin):
149149
150150
Possible issues: for certain time-consuming functions (like simplify)
151151
which are based on sympy or other libraries, it is possible that
152-
the evaluation continues after the timeout. However, at the end of the evaluation, the function will return $\$Aborted$ and the results will not affect
152+
the evaluation continues after the timeout. However, at the end of the evaluation, the function will return $\\$Aborted$ and the results will not affect
153153
the state of the mathics kernel.
154154
155155
"""

0 commit comments

Comments
 (0)