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