Path: blob/develop/src/sage_setup/autogen/interpreters/internal/specs/rr.py
4086 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# https://www.gnu.org/licenses/9# ****************************************************************************10from .base import StackInterpreter11from .python import MemoryChunkPyConstant12from ..instructions import (params_gen, instr_funcall_1arg_mpfr,13instr_funcall_2args_mpfr, InstrSpec)14from ..memory import MemoryChunk, MemoryChunkConstants15from ..storage import ty_mpfr, ty_python16from ..utils import je, reindent_lines as ri171819class MemoryChunkRRRetval(MemoryChunk):20r"""21A special-purpose memory chunk, for dealing with the return value22of the RR-based interpreter.23"""2425def declare_class_members(self):26r"""27Return a string giving the declarations of the class members28in a wrapper class for this memory chunk.2930EXAMPLES::3132sage: from sage_setup.autogen.interpreters.internal import *33sage: from sage_setup.autogen.interpreters.internal.specs.rr import *34sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)35sage: mc.declare_class_members()36''37"""38return ""3940def declare_call_locals(self):41r"""42Return a string to put in the __call__ method of a wrapper43class using this memory chunk, to allocate local variables.4445EXAMPLES::4647sage: from sage_setup.autogen.interpreters.internal import *48sage: from sage_setup.autogen.interpreters.internal.specs.rr import *49sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)50sage: mc.declare_call_locals()51' cdef RealNumber retval = (self.domain)()\n'52"""53return je(ri(8,54"""55cdef RealNumber {{ myself.name }} = (self.domain)()56"""), myself=self)5758def declare_parameter(self):59r"""60Return the string to use to declare the interpreter parameter61corresponding to this memory chunk.6263EXAMPLES::6465sage: from sage_setup.autogen.interpreters.internal import *66sage: from sage_setup.autogen.interpreters.internal.specs.rr import *67sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)68sage: mc.declare_parameter()69'mpfr_t retval'70"""71return '%s %s' % (self.storage_type.c_reference_type(), self.name)7273def pass_argument(self):74r"""75Return the string to pass the argument corresponding to this76memory chunk to the interpreter.7778EXAMPLES::7980sage: from sage_setup.autogen.interpreters.internal import *81sage: from sage_setup.autogen.interpreters.internal.specs.rr import *82sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)83sage: mc.pass_argument()84'retval.value'85"""86return je("""{{ myself.name }}.value""", myself=self)8788def pass_call_c_argument(self):89r"""90Return the string to pass the argument corresponding to this91memory chunk to the interpreter, for use in the call_c method.9293EXAMPLES::9495sage: from sage_setup.autogen.interpreters.internal import *96sage: from sage_setup.autogen.interpreters.internal.specs.rr import *97sage: mc = MemoryChunkRRRetval('retval', ty_mpfr)98sage: mc.pass_call_c_argument()99'result'100"""101return "result"102103104class RRInterpreter(StackInterpreter):105r"""106A subclass of StackInterpreter, specifying an interpreter over107MPFR arbitrary-precision floating-point numbers.108"""109110name = 'rr'111112def __init__(self):113r"""114Initialize an RDFInterpreter.115116EXAMPLES::117118sage: from sage_setup.autogen.interpreters.internal import *119sage: from sage_setup.autogen.interpreters.internal.specs.rr import *120sage: interp = RRInterpreter()121sage: interp.name122'rr'123sage: interp.mc_py_constants124{MC:py_constants}125sage: interp.chunks126[{MC:args}, {MC:retval}, {MC:constants}, {MC:py_constants}, {MC:stack}, {MC:code}, {MC:domain}]127sage: interp.pg('A[D]', 'S')128([({MC:args}, {MC:code}, None)], [({MC:stack}, None, None)])129sage: instrs = dict([(ins.name, ins) for ins in interp.instr_descs])130sage: instrs['add']131add: SS->S = 'mpfr_add(o0, i0, i1, MPFR_RNDN);'132sage: instrs['py_call']133py_call: *->S = '\nif (!rr_py_call_h...goto error;\n}\n'134135That py_call instruction is particularly interesting, and136demonstrates a useful technique to let you use Cython code137in an interpreter. Let's look more closely::138139sage: print(instrs['py_call'].code)140if (!rr_py_call_helper(domain, i0, n_i1, i1, o0)) {141goto error;142}143144This instruction makes use of the function ``rr_py_call_helper``,145which is declared in ``wrapper_rr.h``::146147sage: print(interp.c_header)148<BLANKLINE>149#include <mpfr.h>150<BLANKLINE>151152The function ``rr_py_call_helper`` is implemented in Cython::153154sage: print(interp.pyx_header)155cdef public bint rr_py_call_helper(object domain, object fn,156int n_args,157mpfr_t* args, mpfr_t retval) except 0:158py_args = []159cdef int i160cdef RealNumber rn161for i from 0 <= i < n_args:162rn = domain()163mpfr_set(rn.value, args[i], MPFR_RNDN)164py_args.append(rn)165cdef RealNumber result = domain(fn(*py_args))166mpfr_set(retval, result.value, MPFR_RNDN)167return 1168169So instructions where you need to interact with Python can170call back into Cython code fairly easily.171"""172173mc_retval = MemoryChunkRRRetval('retval', ty_mpfr)174super(RRInterpreter, self).__init__(ty_mpfr, mc_retval=mc_retval)175self.err_return = '0'176self.mc_py_constants = MemoryChunkConstants('py_constants', ty_python)177self.mc_domain = MemoryChunkPyConstant('domain')178self.chunks = [self.mc_args, self.mc_retval, self.mc_constants,179self.mc_py_constants,180self.mc_stack, self.mc_code, self.mc_domain]181pg = params_gen(A=self.mc_args, C=self.mc_constants, D=self.mc_code,182S=self.mc_stack,183P=self.mc_py_constants)184self.pg = pg185self.c_header = ri(0,186'''187#include <mpfr.h>188''')189190self.pxd_header = ri(0,191"""192from sage.rings.real_mpfr cimport RealField_class, RealNumber193from sage.libs.mpfr cimport *194195""")196197self.pyx_header = ri(0,198"""\199cdef public bint rr_py_call_helper(object domain, object fn,200int n_args,201mpfr_t* args, mpfr_t retval) except 0:202py_args = []203cdef int i204cdef RealNumber rn205for i from 0 <= i < n_args:206rn = domain()207mpfr_set(rn.value, args[i], MPFR_RNDN)208py_args.append(rn)209cdef RealNumber result = domain(fn(*py_args))210mpfr_set(retval, result.value, MPFR_RNDN)211return 1212""")213214instrs = [215InstrSpec('load_arg', pg('A[D]', 'S'),216code='mpfr_set(o0, i0, MPFR_RNDN);'),217InstrSpec('load_const', pg('C[D]', 'S'),218code='mpfr_set(o0, i0, MPFR_RNDN);'),219InstrSpec('return', pg('S', ''),220code='mpfr_set(retval, i0, MPFR_RNDN);\nreturn 1;\n'),221InstrSpec('py_call', pg('P[D]S@D', 'S'),222uses_error_handler=True,223code=ri(0,224"""225if (!rr_py_call_helper(domain, i0, n_i1, i1, o0)) {226goto error;227}228"""))229]230for (name, op) in [('add', 'mpfr_add'), ('sub', 'mpfr_sub'),231('mul', 'mpfr_mul'), ('div', 'mpfr_div'),232('pow', 'mpfr_pow')]:233instrs.append(instr_funcall_2args_mpfr(name, pg('SS', 'S'), op))234instrs.append(instr_funcall_2args_mpfr('ipow', pg('SD', 'S'), 'mpfr_pow_si'))235for name in ['neg', 'abs',236'log', 'log2', 'log10',237'exp', 'exp2', 'exp10',238'cos', 'sin', 'tan',239'sec', 'csc', 'cot',240'acos', 'asin', 'atan',241'cosh', 'sinh', 'tanh',242'sech', 'csch', 'coth',243'acosh', 'asinh', 'atanh',244'log1p', 'expm1', 'eint',245'gamma', 'lngamma',246'zeta', 'erf', 'erfc',247'j0', 'j1', 'y0', 'y1']:248instrs.append(instr_funcall_1arg_mpfr(name, pg('S', 'S'), 'mpfr_' + name))249# mpfr_ui_div constructs a temporary mpfr_t and then calls mpfr_div;250# it would probably be (slightly) faster to use a permanent copy251# of "one" (on the other hand, the constructed temporary copy is252# on the stack, so it's very likely to be in the cache).253instrs.append(InstrSpec('invert', pg('S', 'S'),254code='mpfr_ui_div(o0, 1, i0, MPFR_RNDN);'))255self.instr_descs = instrs256self._set_opcodes()257# Supported for exponents that fit in a long, so we could use258# a much wider range on a 64-bit machine. On the other hand,259# it's easier to write the code this way, and constant integer260# exponents outside this range probably aren't very common anyway.261self.ipow_range = (int(-2**31), int(2**31-1))262263264