Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage_setup/autogen/interpreters/internal/specs/rr.py
7405 views
1
# ****************************************************************************
2
# Copyright (C) 2009 Carl Witty <[email protected]>
3
# Copyright (C) 2015 Jeroen Demeyer <[email protected]>
4
#
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, either version 2 of the License, or
8
# (at your option) any later version.
9
# https://www.gnu.org/licenses/
10
# ****************************************************************************
11
from ..instructions import (
12
InstrSpec,
13
instr_funcall_1arg_mpfr,
14
instr_funcall_2args_mpfr,
15
params_gen,
16
)
17
from ..memory import MemoryChunk, MemoryChunkConstants
18
from ..storage import ty_mpfr, ty_python
19
from ..utils import je
20
from ..utils import reindent_lines as ri
21
from .base import StackInterpreter
22
from .python import MemoryChunkPyConstant
23
24
25
class MemoryChunkRRRetval(MemoryChunk):
26
r"""
27
A special-purpose memory chunk, for dealing with the return value
28
of the RR-based interpreter.
29
"""
30
31
def declare_class_members(self):
32
r"""
33
Return a string giving the declarations of the class members
34
in a wrapper class for this memory chunk.
35
36
EXAMPLES::
37
38
sage: from sage_setup.autogen.interpreters.internal import *
39
sage: from sage_setup.autogen.interpreters.internal.specs.rr import *
40
sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)
41
sage: mc.declare_class_members()
42
''
43
"""
44
return ""
45
46
def declare_call_locals(self):
47
r"""
48
Return a string to put in the __call__ method of a wrapper
49
class using this memory chunk, to allocate local variables.
50
51
EXAMPLES::
52
53
sage: from sage_setup.autogen.interpreters.internal import *
54
sage: from sage_setup.autogen.interpreters.internal.specs.rr import *
55
sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)
56
sage: mc.declare_call_locals()
57
' cdef RealNumber retval = (self.domain)()\n'
58
"""
59
return je(ri(8,
60
"""
61
cdef RealNumber {{ myself.name }} = (self.domain)()
62
"""), myself=self)
63
64
def declare_parameter(self):
65
r"""
66
Return the string to use to declare the interpreter parameter
67
corresponding to this memory chunk.
68
69
EXAMPLES::
70
71
sage: from sage_setup.autogen.interpreters.internal import *
72
sage: from sage_setup.autogen.interpreters.internal.specs.rr import *
73
sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)
74
sage: mc.declare_parameter()
75
'mpfr_t retval'
76
"""
77
return '%s %s' % (self.storage_type.c_reference_type(), self.name)
78
79
def pass_argument(self):
80
r"""
81
Return the string to pass the argument corresponding to this
82
memory chunk to the interpreter.
83
84
EXAMPLES::
85
86
sage: from sage_setup.autogen.interpreters.internal import *
87
sage: from sage_setup.autogen.interpreters.internal.specs.rr import *
88
sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)
89
sage: mc.pass_argument()
90
'retval.value'
91
"""
92
return je("""{{ myself.name }}.value""", myself=self)
93
94
def pass_call_c_argument(self):
95
r"""
96
Return the string to pass the argument corresponding to this
97
memory chunk to the interpreter, for use in the call_c method.
98
99
EXAMPLES::
100
101
sage: from sage_setup.autogen.interpreters.internal import *
102
sage: from sage_setup.autogen.interpreters.internal.specs.rr import *
103
sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)
104
sage: mc.pass_call_c_argument()
105
'result'
106
"""
107
return "result"
108
109
110
class RRInterpreter(StackInterpreter):
111
r"""
112
A subclass of StackInterpreter, specifying an interpreter over
113
MPFR arbitrary-precision floating-point numbers.
114
"""
115
116
name = 'rr'
117
118
def __init__(self):
119
r"""
120
Initialize an RDFInterpreter.
121
122
EXAMPLES::
123
124
sage: from sage_setup.autogen.interpreters.internal import *
125
sage: from sage_setup.autogen.interpreters.internal.specs.rr import *
126
sage: interp = RRInterpreter()
127
sage: interp.name
128
'rr'
129
sage: interp.mc_py_constants
130
{MC:py_constants}
131
sage: interp.chunks
132
[{MC:args}, {MC:retval}, {MC:constants}, {MC:py_constants}, {MC:stack}, {MC:code}, {MC:domain}]
133
sage: interp.pg('A[D]', 'S')
134
([({MC:args}, {MC:code}, None)], [({MC:stack}, None, None)])
135
sage: instrs = dict([(ins.name, ins) for ins in interp.instr_descs])
136
sage: instrs['add']
137
add: SS->S = 'mpfr_add(o0, i0, i1, MPFR_RNDN);'
138
sage: instrs['py_call']
139
py_call: *->S = '\nif (!rr_py_call_h...goto error;\n}\n'
140
141
That py_call instruction is particularly interesting, and
142
demonstrates a useful technique to let you use Cython code
143
in an interpreter. Let's look more closely::
144
145
sage: print(instrs['py_call'].code)
146
if (!rr_py_call_helper(domain, i0, n_i1, i1, o0)) {
147
goto error;
148
}
149
150
This instruction makes use of the function ``rr_py_call_helper``,
151
which is declared in ``wrapper_rr.h``::
152
153
sage: print(interp.c_header)
154
<BLANKLINE>
155
#include <mpfr.h>
156
<BLANKLINE>
157
158
The function ``rr_py_call_helper`` is implemented in Cython::
159
160
sage: print(interp.pyx_header)
161
cdef public bint rr_py_call_helper(object domain, object fn,
162
int n_args,
163
mpfr_t* args, mpfr_t retval) except 0:
164
py_args = []
165
cdef int i
166
cdef RealNumber rn
167
for i from 0 <= i < n_args:
168
rn = domain()
169
mpfr_set(rn.value, args[i], MPFR_RNDN)
170
py_args.append(rn)
171
cdef RealNumber result = domain(fn(*py_args))
172
mpfr_set(retval, result.value, MPFR_RNDN)
173
return 1
174
175
So instructions where you need to interact with Python can
176
call back into Cython code fairly easily.
177
"""
178
179
mc_retval = MemoryChunkRRRetval('retval', ty_mpfr)
180
super().__init__(ty_mpfr, mc_retval=mc_retval)
181
self.err_return = '0'
182
self.mc_py_constants = MemoryChunkConstants('py_constants', ty_python)
183
self.mc_domain = MemoryChunkPyConstant('domain')
184
self.chunks = [self.mc_args, self.mc_retval, self.mc_constants,
185
self.mc_py_constants,
186
self.mc_stack, self.mc_code, self.mc_domain]
187
pg = params_gen(A=self.mc_args, C=self.mc_constants, D=self.mc_code,
188
S=self.mc_stack,
189
P=self.mc_py_constants)
190
self.pg = pg
191
self.c_header = ri(0,
192
'''
193
#include <mpfr.h>
194
''')
195
196
self.pxd_header = ri(0,
197
"""
198
from sage.rings.real_mpfr cimport RealField_class, RealNumber
199
from sage.libs.mpfr cimport *
200
201
""")
202
203
self.pyx_header = ri(0,
204
"""\
205
cdef public bint rr_py_call_helper(object domain, object fn,
206
int n_args,
207
mpfr_t* args, mpfr_t retval) except 0:
208
py_args = []
209
cdef int i
210
cdef RealNumber rn
211
for i from 0 <= i < n_args:
212
rn = domain()
213
mpfr_set(rn.value, args[i], MPFR_RNDN)
214
py_args.append(rn)
215
cdef RealNumber result = domain(fn(*py_args))
216
mpfr_set(retval, result.value, MPFR_RNDN)
217
return 1
218
""")
219
220
instrs = [
221
InstrSpec('load_arg', pg('A[D]', 'S'),
222
code='mpfr_set(o0, i0, MPFR_RNDN);'),
223
InstrSpec('load_const', pg('C[D]', 'S'),
224
code='mpfr_set(o0, i0, MPFR_RNDN);'),
225
InstrSpec('return', pg('S', ''),
226
code='mpfr_set(retval, i0, MPFR_RNDN);\nreturn 1;\n'),
227
InstrSpec('py_call', pg('P[D]S@D', 'S'),
228
uses_error_handler=True,
229
code=ri(0,
230
"""
231
if (!rr_py_call_helper(domain, i0, n_i1, i1, o0)) {
232
goto error;
233
}
234
"""))
235
]
236
for (name, op) in [('add', 'mpfr_add'), ('sub', 'mpfr_sub'),
237
('mul', 'mpfr_mul'), ('div', 'mpfr_div'),
238
('pow', 'mpfr_pow')]:
239
instrs.append(instr_funcall_2args_mpfr(name, pg('SS', 'S'), op))
240
instrs.append(instr_funcall_2args_mpfr('ipow', pg('SD', 'S'), 'mpfr_pow_si'))
241
for name in ['neg', 'abs',
242
'log', 'log2', 'log10',
243
'exp', 'exp2', 'exp10',
244
'cos', 'sin', 'tan',
245
'sec', 'csc', 'cot',
246
'acos', 'asin', 'atan',
247
'cosh', 'sinh', 'tanh',
248
'sech', 'csch', 'coth',
249
'acosh', 'asinh', 'atanh',
250
'log1p', 'expm1', 'eint',
251
'gamma', 'lngamma',
252
'zeta', 'erf', 'erfc',
253
'j0', 'j1', 'y0', 'y1']:
254
instrs.append(instr_funcall_1arg_mpfr(name, pg('S', 'S'), 'mpfr_' + name))
255
# mpfr_ui_div constructs a temporary mpfr_t and then calls mpfr_div;
256
# it would probably be (slightly) faster to use a permanent copy
257
# of "one" (on the other hand, the constructed temporary copy is
258
# on the stack, so it's very likely to be in the cache).
259
instrs.append(InstrSpec('invert', pg('S', 'S'),
260
code='mpfr_ui_div(o0, 1, i0, MPFR_RNDN);'))
261
self.instr_descs = instrs
262
self._set_opcodes()
263
# Supported for exponents that fit in a long, so we could use
264
# a much wider range on a 64-bit machine. On the other hand,
265
# it's easier to write the code this way, and constant integer
266
# exponents outside this range probably aren't very common anyway.
267
self.ipow_range = (int(-2**31), int(2**31-1))
268
269