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
2525def 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
4549def 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
4962def 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
98140class 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}
0 commit comments