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