Path: blob/develop/src/sage_setup/autogen/interpreters/internal/specs/cdf.py
7407 views
#*****************************************************************************1# Copyright (C) 2009 Carl Witty <[email protected]>2# Copyright (C) 2015 Jeroen Demeyer <[email protected]>3#4# This program is free software: you can redistribute it and/or modify5# it under the terms of the GNU General Public License as published by6# the Free Software Foundation, either version 2 of the License, or7# (at your option) any later version.8# http://www.gnu.org/licenses/9#*****************************************************************************101112from ..instructions import (13InstrSpec,14instr_funcall_2args,15instr_infix,16instr_unary,17params_gen,18)19from ..memory import MemoryChunkConstants20from ..storage import ty_double_complex, ty_python21from ..utils import reindent_lines as ri22from .base import StackInterpreter232425class CDFInterpreter(StackInterpreter):26r"""27A subclass of StackInterpreter, specifying an interpreter over28complex machine-floating-point values (C doubles).29"""3031name = 'cdf'3233def __init__(self):34r"""35Initialize a CDFInterpreter.3637EXAMPLES::3839sage: from sage_setup.autogen.interpreters.internal import *40sage: from sage_setup.autogen.interpreters.internal.specs.cdf import *41sage: interp = CDFInterpreter()42sage: interp.name43'cdf'44sage: interp.mc_py_constants45{MC:py_constants}46sage: interp.chunks47[{MC:args}, {MC:constants}, {MC:py_constants}, {MC:stack}, {MC:code}]48sage: interp.pg('A[D]', 'S')49([({MC:args}, {MC:code}, None)], [({MC:stack}, None, None)])50sage: instrs = dict([(ins.name, ins) for ins in interp.instr_descs])51sage: instrs['add']52add: SS->S = 'o0 = i0 + i1;'53sage: instrs['sin']54sin: S->S = 'o0 = csin(i0);'55sage: instrs['py_call']56py_call: *->S = '\nif (!cdf_py_call_...goto error;\n}\n'5758A test of integer powers::5960sage: f(x) = sum(x^k for k in [-20..20])61sage: f(CDF(1+2j)) # rel tol 4e-1662-10391778.999999996 + 3349659.499999962*I63sage: ff = fast_callable(f, CDF)64sage: ff(1 + 2j) # rel tol 1e-1465-10391779.000000004 + 3349659.49999997*I66sage: ff.python_calls()67[]6869sage: f(x) = sum(x^k for k in [0..5])70sage: ff = fast_callable(f, CDF)71sage: ff(2)7263.073sage: ff(2j)7413.0 + 26.0*I75"""7677super().__init__(ty_double_complex)78self.mc_py_constants = MemoryChunkConstants('py_constants', ty_python)79# See comment for RDFInterpreter80self.err_return = '-1094648119105371'81self.adjust_retval = "dz_to_CDE"82self.chunks = [self.mc_args, self.mc_constants, self.mc_py_constants,83self.mc_stack,84self.mc_code]85pg = params_gen(A=self.mc_args, C=self.mc_constants, D=self.mc_code,86S=self.mc_stack, P=self.mc_py_constants)87self.pg = pg88self.c_header = ri(0,"""89#include <stdlib.h>90#include <complex.h>9192/* On Solaris, we need to define _Imaginary_I when compiling with GCC,93* otherwise the constant I doesn't work. The definition below is based94* on glibc. */95#ifdef __GNUC__96#undef _Imaginary_I97#define _Imaginary_I (__extension__ 1.0iF)98#endif99100typedef double complex double_complex;101102static inline double complex csquareX(double complex z) {103double complex res;104__real__(res) = __real__(z) * __real__(z) - __imag__(z) * __imag__(z);105__imag__(res) = 2 * __real__(z) * __imag__(z);106return res;107}108109static inline double complex cpow_int(double complex z, int exp) {110if (exp < 0) return 1/cpow_int(z, -exp);111switch (exp) {112case 0: return 1;113case 1: return z;114case 2: return csquareX(z);115case 3: return csquareX(z) * z;116case 4:117case 5:118case 6:119case 7:120case 8:121{122double complex z2 = csquareX(z);123double complex z4 = csquareX(z2);124if (exp == 4) return z4;125if (exp == 5) return z4 * z;126if (exp == 6) return z4 * z2;127if (exp == 7) return z4 * z2 * z;128if (exp == 8) return z4 * z4;129}130}131if (cimag(z) == 0) return pow(creal(z), exp);132if (creal(z) == 0) {133double r = pow(cimag(z), exp);134switch (exp % 4) {135case 0:136return r;137case 1:138return r * I;139case 2:140return -r;141default /* case 3 */:142return -r * I;143}144}145return cpow(z, exp);146}147""")148149self.pxd_header = ri(0, """150# This is to work around a header incompatibility with PARI using151# "I" as variable conflicting with the complex "I".152# If we cimport pari earlier, we avoid this problem.153cimport cypari2.types154155# We need the type double_complex to work around156# http://trac.cython.org/ticket/869157# so this is a bit hackish.158cdef extern from "complex.h":159ctypedef double double_complex "double complex"160""")161162self.pyx_header = ri(0, """163from sage.libs.gsl.complex cimport *164from sage.rings.complex_double cimport ComplexDoubleElement165import sage.rings.complex_double166cdef object CDF = sage.rings.complex_double.CDF167168cdef extern from "complex.h":169cdef double creal(double_complex)170cdef double cimag(double_complex)171cdef double_complex _Complex_I172173cdef inline double_complex CDE_to_dz(zz) noexcept:174cdef ComplexDoubleElement z = <ComplexDoubleElement>(zz if isinstance(zz, ComplexDoubleElement) else CDF(zz))175return GSL_REAL(z._complex) + _Complex_I * GSL_IMAG(z._complex)176177cdef inline ComplexDoubleElement dz_to_CDE(double_complex dz):178cdef ComplexDoubleElement z = <ComplexDoubleElement>ComplexDoubleElement.__new__(ComplexDoubleElement)179GSL_SET_COMPLEX(&z._complex, creal(dz), cimag(dz))180return z181182cdef public bint cdf_py_call_helper(object fn,183int n_args,184double_complex* args, double_complex* retval) except 0:185py_args = []186cdef int i187for i from 0 <= i < n_args:188py_args.append(dz_to_CDE(args[i]))189py_result = fn(*py_args)190cdef ComplexDoubleElement result191if isinstance(py_result, ComplexDoubleElement):192result = <ComplexDoubleElement>py_result193else:194result = CDF(py_result)195retval[0] = CDE_to_dz(result)196return 1197"""[1:])198199instrs = [200InstrSpec('load_arg', pg('A[D]', 'S'),201code='o0 = i0;'),202InstrSpec('load_const', pg('C[D]', 'S'),203code='o0 = i0;'),204InstrSpec('return', pg('S', ''),205code='return i0;'),206InstrSpec('py_call', pg('P[D]S@D', 'S'),207uses_error_handler=True,208code="""209if (!cdf_py_call_helper(i0, n_i1, i1, &o0)) {210goto error;211}212""")213]214for name, op in [('add', '+'), ('sub', '-'),215('mul', '*'), ('div', '/'),216('truediv', '/')]:217instrs.append(instr_infix(name, pg('SS', 'S'), op))218instrs.append(instr_funcall_2args('pow', pg('SS', 'S'), 'cpow'))219instrs.append(instr_funcall_2args('ipow', pg('SD', 'S'), 'cpow_int'))220for (name, op) in [('neg', '-i0'), ('invert', '1/i0'),221('abs', 'cabs(i0)')]:222instrs.append(instr_unary(name, pg('S', 'S'), op))223for name in ['sqrt', 'sin', 'cos', 'tan',224'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh',225'asinh', 'acosh', 'atanh', 'exp', 'log']:226instrs.append(instr_unary(name, pg('S', 'S'), "c%s(i0)" % name))227self.instr_descs = instrs228self._set_opcodes()229# supported for exponents that fit in an int230self.ipow_range = (int(-2**31), int(2**31 - 1))231232233