Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage_setup/autogen/interpreters/internal/specs/cdf.py
7407 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
# http://www.gnu.org/licenses/
10
#*****************************************************************************
11
12
13
from ..instructions import (
14
InstrSpec,
15
instr_funcall_2args,
16
instr_infix,
17
instr_unary,
18
params_gen,
19
)
20
from ..memory import MemoryChunkConstants
21
from ..storage import ty_double_complex, ty_python
22
from ..utils import reindent_lines as ri
23
from .base import StackInterpreter
24
25
26
class CDFInterpreter(StackInterpreter):
27
r"""
28
A subclass of StackInterpreter, specifying an interpreter over
29
complex machine-floating-point values (C doubles).
30
"""
31
32
name = 'cdf'
33
34
def __init__(self):
35
r"""
36
Initialize a CDFInterpreter.
37
38
EXAMPLES::
39
40
sage: from sage_setup.autogen.interpreters.internal import *
41
sage: from sage_setup.autogen.interpreters.internal.specs.cdf import *
42
sage: interp = CDFInterpreter()
43
sage: interp.name
44
'cdf'
45
sage: interp.mc_py_constants
46
{MC:py_constants}
47
sage: interp.chunks
48
[{MC:args}, {MC:constants}, {MC:py_constants}, {MC:stack}, {MC:code}]
49
sage: interp.pg('A[D]', 'S')
50
([({MC:args}, {MC:code}, None)], [({MC:stack}, None, None)])
51
sage: instrs = dict([(ins.name, ins) for ins in interp.instr_descs])
52
sage: instrs['add']
53
add: SS->S = 'o0 = i0 + i1;'
54
sage: instrs['sin']
55
sin: S->S = 'o0 = csin(i0);'
56
sage: instrs['py_call']
57
py_call: *->S = '\nif (!cdf_py_call_...goto error;\n}\n'
58
59
A test of integer powers::
60
61
sage: f(x) = sum(x^k for k in [-20..20])
62
sage: f(CDF(1+2j)) # rel tol 4e-16
63
-10391778.999999996 + 3349659.499999962*I
64
sage: ff = fast_callable(f, CDF)
65
sage: ff(1 + 2j) # rel tol 1e-14
66
-10391779.000000004 + 3349659.49999997*I
67
sage: ff.python_calls()
68
[]
69
70
sage: f(x) = sum(x^k for k in [0..5])
71
sage: ff = fast_callable(f, CDF)
72
sage: ff(2)
73
63.0
74
sage: ff(2j)
75
13.0 + 26.0*I
76
"""
77
78
super().__init__(ty_double_complex)
79
self.mc_py_constants = MemoryChunkConstants('py_constants', ty_python)
80
# See comment for RDFInterpreter
81
self.err_return = '-1094648119105371'
82
self.adjust_retval = "dz_to_CDE"
83
self.chunks = [self.mc_args, self.mc_constants, self.mc_py_constants,
84
self.mc_stack,
85
self.mc_code]
86
pg = params_gen(A=self.mc_args, C=self.mc_constants, D=self.mc_code,
87
S=self.mc_stack, P=self.mc_py_constants)
88
self.pg = pg
89
self.c_header = ri(0,"""
90
#include <stdlib.h>
91
#include <complex.h>
92
93
/* On Solaris, we need to define _Imaginary_I when compiling with GCC,
94
* otherwise the constant I doesn't work. The definition below is based
95
* on glibc. */
96
#ifdef __GNUC__
97
#undef _Imaginary_I
98
#define _Imaginary_I (__extension__ 1.0iF)
99
#endif
100
101
typedef double complex double_complex;
102
103
static inline double complex csquareX(double complex z) {
104
double complex res;
105
__real__(res) = __real__(z) * __real__(z) - __imag__(z) * __imag__(z);
106
__imag__(res) = 2 * __real__(z) * __imag__(z);
107
return res;
108
}
109
110
static inline double complex cpow_int(double complex z, int exp) {
111
if (exp < 0) return 1/cpow_int(z, -exp);
112
switch (exp) {
113
case 0: return 1;
114
case 1: return z;
115
case 2: return csquareX(z);
116
case 3: return csquareX(z) * z;
117
case 4:
118
case 5:
119
case 6:
120
case 7:
121
case 8:
122
{
123
double complex z2 = csquareX(z);
124
double complex z4 = csquareX(z2);
125
if (exp == 4) return z4;
126
if (exp == 5) return z4 * z;
127
if (exp == 6) return z4 * z2;
128
if (exp == 7) return z4 * z2 * z;
129
if (exp == 8) return z4 * z4;
130
}
131
}
132
if (cimag(z) == 0) return pow(creal(z), exp);
133
if (creal(z) == 0) {
134
double r = pow(cimag(z), exp);
135
switch (exp % 4) {
136
case 0:
137
return r;
138
case 1:
139
return r * I;
140
case 2:
141
return -r;
142
default /* case 3 */:
143
return -r * I;
144
}
145
}
146
return cpow(z, exp);
147
}
148
""")
149
150
self.pxd_header = ri(0, """
151
# This is to work around a header incompatibility with PARI using
152
# "I" as variable conflicting with the complex "I".
153
# If we cimport pari earlier, we avoid this problem.
154
cimport cypari2.types
155
156
# We need the type double_complex to work around
157
# http://trac.cython.org/ticket/869
158
# so this is a bit hackish.
159
cdef extern from "complex.h":
160
ctypedef double double_complex "double complex"
161
""")
162
163
self.pyx_header = ri(0, """
164
from sage.libs.gsl.complex cimport *
165
from sage.rings.complex_double cimport ComplexDoubleElement
166
import sage.rings.complex_double
167
cdef object CDF = sage.rings.complex_double.CDF
168
169
cdef extern from "complex.h":
170
cdef double creal(double_complex)
171
cdef double cimag(double_complex)
172
cdef double_complex _Complex_I
173
174
cdef inline double_complex CDE_to_dz(zz) noexcept:
175
cdef ComplexDoubleElement z = <ComplexDoubleElement>(zz if isinstance(zz, ComplexDoubleElement) else CDF(zz))
176
return GSL_REAL(z._complex) + _Complex_I * GSL_IMAG(z._complex)
177
178
cdef inline ComplexDoubleElement dz_to_CDE(double_complex dz):
179
cdef ComplexDoubleElement z = <ComplexDoubleElement>ComplexDoubleElement.__new__(ComplexDoubleElement)
180
GSL_SET_COMPLEX(&z._complex, creal(dz), cimag(dz))
181
return z
182
183
cdef public bint cdf_py_call_helper(object fn,
184
int n_args,
185
double_complex* args, double_complex* retval) except 0:
186
py_args = []
187
cdef int i
188
for i from 0 <= i < n_args:
189
py_args.append(dz_to_CDE(args[i]))
190
py_result = fn(*py_args)
191
cdef ComplexDoubleElement result
192
if isinstance(py_result, ComplexDoubleElement):
193
result = <ComplexDoubleElement>py_result
194
else:
195
result = CDF(py_result)
196
retval[0] = CDE_to_dz(result)
197
return 1
198
"""[1:])
199
200
instrs = [
201
InstrSpec('load_arg', pg('A[D]', 'S'),
202
code='o0 = i0;'),
203
InstrSpec('load_const', pg('C[D]', 'S'),
204
code='o0 = i0;'),
205
InstrSpec('return', pg('S', ''),
206
code='return i0;'),
207
InstrSpec('py_call', pg('P[D]S@D', 'S'),
208
uses_error_handler=True,
209
code="""
210
if (!cdf_py_call_helper(i0, n_i1, i1, &o0)) {
211
goto error;
212
}
213
""")
214
]
215
for name, op in [('add', '+'), ('sub', '-'),
216
('mul', '*'), ('div', '/'),
217
('truediv', '/')]:
218
instrs.append(instr_infix(name, pg('SS', 'S'), op))
219
instrs.append(instr_funcall_2args('pow', pg('SS', 'S'), 'cpow'))
220
instrs.append(instr_funcall_2args('ipow', pg('SD', 'S'), 'cpow_int'))
221
for (name, op) in [('neg', '-i0'), ('invert', '1/i0'),
222
('abs', 'cabs(i0)')]:
223
instrs.append(instr_unary(name, pg('S', 'S'), op))
224
for name in ['sqrt', 'sin', 'cos', 'tan',
225
'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh',
226
'asinh', 'acosh', 'atanh', 'exp', 'log']:
227
instrs.append(instr_unary(name, pg('S', 'S'), "c%s(i0)" % name))
228
self.instr_descs = instrs
229
self._set_opcodes()
230
# supported for exponents that fit in an int
231
self.ipow_range = (int(-2**31), int(2**31 - 1))
232
233