示例#1
0
    def generate_scalar_scheme(self, vx):
        # approximation the gamma function
        abs_vx = Abs(vx, precision=self.precision)

        FCT_LIMIT = 1.0

        omega_value = self.precision.get_omega()

        def sollya_wrap_bigfloat_fct(bfct):
            """ wrap bigfloat's function <bfct> such that is can be used
                on SollyaObject inputs and returns SollyaObject results """
            def fct(x):
                return sollya.SollyaObject(bfct(SollyaObject(x).bigfloat()))

            return fct

        sollya_gamma = sollya_wrap_bigfloat_fct(bigfloat.gamma)
        sollya_digamma = sollya_wrap_bigfloat_fct(bigfloat.digamma)
        # first derivative of gamma is digamma * gamma
        bigfloat_gamma_d0 = lambda x: bigfloat.gamma(x) * bigfloat.digamma(x)
        sollya_gamma_d0 = sollya_wrap_bigfloat_fct(bigfloat_gamma_d0)

        # approximating trigamma with straightforward derivatives formulae of digamma
        U = 2**-64
        bigfloat_trigamma = lambda x: (
            (bigfloat.digamma(x * (1 + U)) - bigfloat.digamma(x)) / (x * U))
        sollya_trigamma = sollya_wrap_bigfloat_fct(bigfloat_trigamma)

        bigfloat_gamma_d1 = lambda x: (bigfloat_trigamma(x) * bigfloat.gamma(
            x) + bigfloat_gamma_d0(x) * bigfloat.digamma(x))
        sollya_gamma_d1 = sollya_wrap_bigfloat_fct(bigfloat_gamma_d1)

        def sollya_gamma_fct(x, diff_order, prec):
            """ wrapper to use bigfloat implementation of exponential
                rather than sollya's implementation directly.
                This wrapper implements sollya's function API.

                :param x: numerical input value (may be an Interval)
                :param diff_order: differential order
                :param prec: numerical precision expected (min)
            """
            fct = None
            if diff_order == 0:
                fct = sollya_gamma
            elif diff_order == 1:
                fct = sollya_gamma_d0
            elif diff_order == 2:
                fct = sollya_gamma_d1
            else:
                raise NotImplementedError
            with bigfloat.precision(prec):
                if x.is_range():
                    lo = sollya.inf(x)
                    hi = sollya.sup(x)
                    return sollya.Interval(fct(lo), fct(hi))
                else:
                    return fct(x)

        # search the lower x such that gamma(x) >= omega
        omega_upper_limit = search_bound_threshold(sollya_gamma, omega_value,
                                                   2, 1000.0, self.precision)
        Log.report(Log.Debug, "gamma(x) = {} limit is {}", omega_value,
                   omega_upper_limit)

        # evaluate gamma(<min-normal-value>)
        lower_x_bound = self.precision.get_min_normal_value()
        value_min = sollya_gamma(lower_x_bound)
        Log.report(Log.Debug, "gamma({}) = {}(log2={})", lower_x_bound,
                   value_min, int(sollya.log2(value_min)))

        # evaluate gamma(<min-subnormal-value>)
        lower_x_bound = self.precision.get_min_subnormal_value()
        value_min = sollya_gamma(lower_x_bound)
        Log.report(Log.Debug, "gamma({}) = {}(log2={})", lower_x_bound,
                   value_min, int(sollya.log2(value_min)))

        # Gamma is defined such that gamma(x+1) = x * gamma(x)
        #
        # we approximate gamma over [1, 2]
        # y in [1, 2]
        # gamma(y) = (y-1) * gamma(y-1)
        # gamma(y-1) = gamma(y) / (y-1)
        Log.report(Log.Info, "building mathematical polynomial")
        approx_interval = Interval(1, 2)
        approx_fct = sollya.function(sollya_gamma_fct)
        poly_degree = int(
            sup(
                guessdegree(approx_fct, approx_interval, S2**
                            -(self.precision.get_field_size() + 5)))) + 1
        Log.report(Log.Debug, "approximation's poly degree over [1, 2] is {}",
                   poly_degree)

        sys.exit(1)

        poly_degree_list = list(range(1, poly_degree, 2))
        Log.report(Log.Debug, "poly_degree is {} and list {}", poly_degree,
                   poly_degree_list)
        global_poly_object = Polynomial.build_from_approximation(
            approx_fct, poly_degree_list,
            [self.precision] * len(poly_degree_list), approx_interval,
            sollya.relative)
        Log.report(
            Log.Debug, "inform is {}",
            dirtyinfnorm(approx_fct - global_poly_object.get_sollya_object(),
                         approx_interval))
        poly_object = global_poly_object.sub_poly(start_index=1, offset=1)

        ext_precision = {
            ML_Binary32: ML_SingleSingle,
            ML_Binary64: ML_DoubleDouble,
        }[self.precision]

        pre_poly = PolynomialSchemeEvaluator.generate_horner_scheme(
            poly_object, abs_vx, unified_precision=self.precision)

        result = FMA(pre_poly, abs_vx, abs_vx)
        result.set_attributes(tag="result", debug=debug_multi)

        eps_target = S2**-(self.precision.get_field_size() + 5)

        def offset_div_function(fct):
            return lambda offset: fct(sollya.x + offset)

        # empiral numbers
        field_size = {ML_Binary32: 6, ML_Binary64: 8}[self.precision]

        near_indexing = SubFPIndexing(eps_exp, 0, 6, self.precision)
        near_approx = generic_poly_split(offset_div_function(sollya.erf),
                                         near_indexing, eps_target,
                                         self.precision, abs_vx)
        near_approx.set_attributes(tag="near_approx", debug=debug_multi)

        def offset_function(fct):
            return lambda offset: fct(sollya.x + offset)

        medium_indexing = SubFPIndexing(1, one_limit_exp, 7, self.precision)

        medium_approx = generic_poly_split(offset_function(sollya.erf),
                                           medium_indexing, eps_target,
                                           self.precision, abs_vx)
        medium_approx.set_attributes(tag="medium_approx", debug=debug_multi)

        # approximation for positive values
        scheme = ConditionBlock(
            abs_vx < eps, Return(result),
            ConditionBlock(
                abs_vx < near_indexing.get_max_bound(), Return(near_approx),
                ConditionBlock(abs_vx < medium_indexing.get_max_bound(),
                               Return(medium_approx),
                               Return(Constant(1.0,
                                               precision=self.precision)))))
        return scheme
示例#2
0
    def generate_scheme(self):
        vx = self.implementation.add_input_variable("x", self.precision)
        sollya_precision = self.get_input_precision().sollya_object

        # local overloading of RaiseReturn operation
        def ExpRaiseReturn(*args, **kwords):
                kwords["arg_value"] = vx
                kwords["function_name"] = self.function_name
                return RaiseReturn(*args, **kwords)

        # 2-limb approximation of log(2)
        # hi part precision is reduced to provide exact operation
        # when multiplied by an exponent value
        log2_hi_value = round(log(2), self.precision.get_field_size() - (self.precision.get_exponent_size() + 1), sollya.RN)
        log2_lo_value = round(log(2) - log2_hi_value, self.precision.sollya_object, sollya.RN)

        log2_hi = Constant(log2_hi_value, precision=self.precision)
        log2_lo = Constant(log2_lo_value, precision=self.precision)


        int_precision = self.precision.get_integer_format()

        # retrieving processor inverse approximation table
        dummy_var = Variable("dummy", precision = self.precision)
        dummy_rcp_seed = ReciprocalSeed(dummy_var, precision = self.precision)
        inv_approx_table = self.processor.get_recursive_implementation(dummy_rcp_seed, language = None, table_getter = lambda self: self.approx_table_map)

        # table creation
        table_index_size = inv_approx_table.index_size
        log_table = ML_NewTable(dimensions = [2**table_index_size, 2], storage_precision = self.precision)
        # storing accurate logarithm approximation of value returned
        # by the fast reciprocal operation
        for i in range(0, 2**table_index_size):
            inv_value = inv_approx_table[i]
            value_high = round(log(inv_value), self.precision.get_field_size() - (self.precision.get_exponent_size() + 1), sollya.RN)
            value_low = round(log(inv_value) - value_high, sollya_precision, sollya.RN)
            log_table[i][0] = value_high
            log_table[i][1] = value_low


        neg_input = Comparison(vx, -1, likely=False, precision=ML_Bool, specifier=Comparison.Less, debug=debug_multi, tag="neg_input")
        vx_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, precision=ML_Bool, debug=debug_multi, tag="nan_or_inf")
        vx_snan = Test(vx, specifier=Test.IsSignalingNaN, likely=False, debug=debug_multi, tag="snan")
        vx_inf    = Test(vx, specifier=Test.IsInfty, likely=False, debug=debug_multi, tag="inf")
        vx_subnormal = Test(vx, specifier=Test.IsSubnormal, likely=False, debug=debug_multi, tag="vx_subnormal")

        # for x = m.2^e, such that e >= 0
        #
        # log(1+x) = log(1 + m.2^e)
        #          = log(2^e . 2^-e + m.2^e)
        #          = log(2^e . (2^-e + m))
        #          = log(2^e) + log(2^-e + m)
        #          = e . log(2) + log (2^-e + m)
        #
        # t = (2^-e + m)
        # t = m_t . 2^e_t
        # r ~ 1 / m_t   => r.m_t ~ 1 ~ 0
        #
        # t' = t . 2^-e_t
        #    = 2^-e-e_t + m . 2^-e_t
        #
        # if e >= 0, then 2^-e <= 1, then 1 <= m + 2^-e <= 3
        # r = m_r . 2^e_r
        #
        # log(1+x) = e.log(2) + log(r . 2^e_t . 2^-e_t . (2^-e + m) / r)
        #          = e.log(2) + log(r . 2^(-e-e_t) + r.m.2^-e_t) + e_t . log(2)- log(r)
        #          = (e+e_t).log(2) + log(r . t') - log(r)
        #          = (e+e_t).log(2) + log(r . t') - log(r)
        #          = (e+e_t).log(2) + P_log1p(r . t' - 1) - log(r)
        #
        #

        # argument reduction
        m = MantissaExtraction(vx, tag="vx", precision=self.precision, debug=debug_multi)
        e = ExponentExtraction(vx, tag="e", precision=int_precision, debug=debug_multi)

        # 2^-e
        TwoMinusE = ExponentInsertion(-e, tag="Two_minus_e", precision=self.precision, debug=debug_multi)
        t = Addition(TwoMinusE, m, precision=self.precision, tag="t", debug=debug_multi)

        m_t = MantissaExtraction(t, tag="m_t", precision=self.precision, debug=debug_multi)
        e_t = ExponentExtraction(t, tag="e_t", precision=int_precision, debug=debug_multi)

        # 2^(-e-e_t)
        TwoMinusEEt = ExponentInsertion(-e-e_t, tag="Two_minus_e_et", precision=self.precision)
        TwoMinusEt = ExponentInsertion(-e_t, tag="Two_minus_et", precision=self.precision, debug=debug_multi)

        rcp_mt = ReciprocalSeed(m_t, tag="rcp_mt", precision=self.precision, debug=debug_multi)

        INDEX_SIZE = table_index_size
        table_index = generic_mantissa_msb_index_fct(INDEX_SIZE, m_t)
        table_index.set_attributes(tag="table_index", debug=debug_multi)

        log_inv_lo = TableLoad(log_table, table_index, 1, tag="log_inv_lo", debug=debug_multi) 
        log_inv_hi = TableLoad(log_table, table_index, 0, tag="log_inv_hi", debug=debug_multi)

        inv_err = S2**-6 # TODO: link to target DivisionSeed precision

        Log.report(Log.Info, "building mathematical polynomial")
        approx_interval = Interval(-inv_err, inv_err)
        approx_fct = sollya.log1p(sollya.x) / (sollya.x)
        poly_degree = sup(guessdegree(approx_fct, approx_interval, S2**-(self.precision.get_field_size()+1))) + 1
        Log.report(Log.Debug, "poly_degree is {}", poly_degree)
        global_poly_object = Polynomial.build_from_approximation(approx_fct, poly_degree, [self.precision]*(poly_degree+1), approx_interval, sollya.absolute)
        poly_object = global_poly_object # .sub_poly(start_index=1)

        EXT_PRECISION_MAP = {
            ML_Binary32: ML_SingleSingle,
            ML_Binary64: ML_DoubleDouble,
            ML_SingleSingle: ML_TripleSingle,
            ML_DoubleDouble: ML_TripleDouble
        }
        if not self.precision in EXT_PRECISION_MAP:
            Log.report(Log.Error, "no extended precision available for {}", self.precision)

        ext_precision = EXT_PRECISION_MAP[self.precision]

        # pre_rtp = r . 2^(-e-e_t) + m .2^-e_t
        pre_rtp = Addition(
            rcp_mt * TwoMinusEEt,
            Multiplication(
                rcp_mt,
                Multiplication(
                    m,
                    TwoMinusEt,
                    precision=self.precision,
                    tag="pre_mult",
                    debug=debug_multi,
                ),
                precision=ext_precision,
                tag="pre_mult2",
                debug=debug_multi,
            ),
            precision=ext_precision,
            tag="pre_rtp",
            debug=debug_multi
        )
        pre_red_vx = Addition(
            pre_rtp,
            -1,
            precision=ext_precision,
        )

        red_vx = Conversion(pre_red_vx, precision=self.precision, tag="red_vx", debug=debug_multi)

        Log.report(Log.Info, "generating polynomial evaluation scheme")
        poly = PolynomialSchemeEvaluator.generate_horner_scheme(
            poly_object, red_vx, unified_precision=self.precision)

        poly.set_attributes(tag="poly", debug=debug_multi)
        Log.report(Log.Debug, "{}", global_poly_object.get_sollya_object())

        fp_e = Conversion(e + e_t, precision=self.precision, tag="fp_e", debug=debug_multi)


        ext_poly = Multiplication(red_vx, poly, precision=ext_precision)

        pre_result = Addition(
            Addition(
                fp_e * log2_hi,
                fp_e * log2_lo,
                precision=ext_precision
            ),
            Addition(
                Addition(
                    -log_inv_hi,
                    -log_inv_lo,
                    precision=ext_precision
                ),
                ext_poly,
                precision=ext_precision
            ),
            precision=ext_precision
        )

        result = Conversion(pre_result, precision=self.precision, tag="result", debug=debug_multi)


        # main scheme
        Log.report(Log.Info, "MDL scheme")
        pre_scheme = ConditionBlock(neg_input,
            Statement(
                ClearException(),
                Raise(ML_FPE_Invalid),
                Return(FP_QNaN(self.precision))
            ),
            ConditionBlock(vx_nan_or_inf,
                ConditionBlock(vx_inf,
                    Statement(
                        ClearException(),
                        Return(FP_PlusInfty(self.precision)),
                    ),
                    Statement(
                        ClearException(),
                        ConditionBlock(vx_snan,
                            Raise(ML_FPE_Invalid)
                        ),
                        Return(FP_QNaN(self.precision))
                    )
                ),
                Return(result)
            )
        )
        scheme = pre_scheme
        return scheme
示例#3
0
    def generate_scalar_scheme(self, vx):
        abs_vx = Abs(vx, precision=self.precision)

        FCT_LIMIT = 1.0

        one_limit = search_bound_threshold(sollya.erf, FCT_LIMIT, 1.0, 10.0,
                                           self.precision)
        one_limit_exp = int(sollya.floor(sollya.log2(one_limit)))
        Log.report(Log.Debug, "erf(x) = 1.0 limit is {}, with exp={}",
                   one_limit, one_limit_exp)

        upper_approx_bound = 10

        # empiral numbers
        eps_exp = {ML_Binary32: -3, ML_Binary64: -5}[self.precision]
        eps = S2**eps_exp

        Log.report(Log.Info, "building mathematical polynomial")
        approx_interval = Interval(0, eps)
        # fonction to approximate is erf(x) / x
        # it is an even function erf(x) / x = erf(-x) / (-x)
        approx_fct = sollya.erf(sollya.x) - (sollya.x)
        poly_degree = int(
            sup(
                guessdegree(approx_fct, approx_interval, S2**
                            -(self.precision.get_field_size() + 5)))) + 1

        poly_degree_list = list(range(1, poly_degree, 2))
        Log.report(Log.Debug, "poly_degree is {} and list {}", poly_degree,
                   poly_degree_list)
        global_poly_object = Polynomial.build_from_approximation(
            approx_fct, poly_degree_list,
            [self.precision] * len(poly_degree_list), approx_interval,
            sollya.relative)
        Log.report(
            Log.Debug, "inform is {}",
            dirtyinfnorm(approx_fct - global_poly_object.get_sollya_object(),
                         approx_interval))
        poly_object = global_poly_object.sub_poly(start_index=1, offset=1)

        ext_precision = {
            ML_Binary32: ML_SingleSingle,
            ML_Binary64: ML_DoubleDouble,
        }[self.precision]

        pre_poly = PolynomialSchemeEvaluator.generate_horner_scheme(
            poly_object, abs_vx, unified_precision=self.precision)

        result = FMA(pre_poly, abs_vx, abs_vx)
        result.set_attributes(tag="result", debug=debug_multi)

        eps_target = S2**-(self.precision.get_field_size() + 5)

        def offset_div_function(fct):
            return lambda offset: fct(sollya.x + offset)

        # empiral numbers
        field_size = {ML_Binary32: 6, ML_Binary64: 8}[self.precision]

        near_indexing = SubFPIndexing(eps_exp, 0, 6, self.precision)
        near_approx = generic_poly_split(offset_div_function(sollya.erf),
                                         near_indexing, eps_target,
                                         self.precision, abs_vx)
        near_approx.set_attributes(tag="near_approx", debug=debug_multi)

        def offset_function(fct):
            return lambda offset: fct(sollya.x + offset)

        medium_indexing = SubFPIndexing(1, one_limit_exp, 7, self.precision)

        medium_approx = generic_poly_split(offset_function(sollya.erf),
                                           medium_indexing, eps_target,
                                           self.precision, abs_vx)
        medium_approx.set_attributes(tag="medium_approx", debug=debug_multi)

        # approximation for positive values
        scheme = ConditionBlock(
            abs_vx < eps, Return(result),
            ConditionBlock(
                abs_vx < near_indexing.get_max_bound(), Return(near_approx),
                ConditionBlock(abs_vx < medium_indexing.get_max_bound(),
                               Return(medium_approx),
                               Return(Constant(1.0,
                                               precision=self.precision)))))
        return scheme
示例#4
0
if __name__ == "__main__":
    implem_results = []
    for eps_target in [S2**-40, S2**-50, S2**-55, S2**-60, S2**-65]:
        approx_interval = Interval(-S2**-5, S2**-5)
        ctx = MLL_Context(ML_Binary64, approx_interval)
        vx = Variable("x",
                      precision=ctx.variableFormat,
                      interval=approx_interval)
        # guessding the best degree
        poly_degree = int(
            sup(
                sollya.guessdegree(sollya.exp(sollya.x), approx_interval,
                                   eps_target)))
        # asking sollya to provide the approximation
        poly_object = Polynomial.build_from_approximation(
            sollya.exp(sollya.x), poly_degree,
            [sollya.doubledouble] * (poly_degree + 1), vx.interval)
        print("poly object is {}".format(poly_object))
        poly_graph, poly_epsilon = mll_implementpoly_horner(
            ctx, poly_object, eps_target, vx)
        print("poly_graph is {}".format(
            poly_graph.get_str(depth=None, display_precision=True)))
        print("poly epsilon is {}".format(float(poly_epsilon)))
        print("poly accuracy is {}".format(
            get_accuracy_from_epsilon(poly_epsilon)))
        implem_results.append(
            (eps_target, poly_degree, poly_object, poly_graph, poly_epsilon))

    for result in implem_results:
        eps_target, poly_degree, poly_object, poly_graph, poly_epsilon = result
        epsilon_log2 = int(sollya.floor(sollya.log2(poly_epsilon)))