def generate_scheme(self): """ main scheme generation """ Log.report(Log.Info, "width parameter is {}".format(self.width)) int_size = 3 frac_size = self.width - int_size input_precision = fixed_point(int_size, frac_size) output_precision = fixed_point(int_size, frac_size) # declaring main input variable var_x = self.implementation.add_input_signal("x", input_precision) var_y = self.implementation.add_input_signal("y", input_precision) var_x.set_attributes(debug = debug_fixed) var_y.set_attributes(debug = debug_fixed) sub = var_x - var_y c = Constant(0) self.implementation.start_new_stage() #pre_result = Select( # c > sub, # c, # sub #) pre_result = Max(0, sub) self.implementation.start_new_stage() result = Conversion(pre_result + var_x, precision=output_precision) self.implementation.add_output_signal("vr_out", result) return [self.implementation]
def subnormalize(x_list, factor, precision=None, fma=True): """ x_list is a multi-component number with components ordered from the most to the least siginificant. x_list[0] must be the rounded evaluation of (x_list[0] + x_list[1] + ...) @return the field of x as a floating-point number assuming the exponent of the result is exponent(x) + factor and managing field subnormalization if required """ x_hi = x_list[0] int_precision = precision.get_integer_format() ex = ExponentExtraction(x_hi, precision=int_precision) scaled_ex = ex + factor # difference betwen x's real exponent and the minimal exponent # for a floating of format precision CI0 = Constant(0, precision=int_precision) CI1 = Constant(1, precision=int_precision) delta = Max(Min(precision.get_emin() - scaled_ex, CI0), Constant(precision.get_field_size(), precision=int_precision)) casted_int_x = TypeCast(x_hi, precision=int_precision) # compute a constant to be added to a casted floating-point to perform # rounding. This constant shall be equivalent to a half-ulp round_cst = BitLogicLeftShift(CI1, delta - 1, precision=int_precision) pre_rounded_value = TypeCast(casted_int_x + round_cst, precision=precision) sticky_shift = precision.get_bit_size() - (delta - 1) sticky = BitLogicLeftShift(casted_int_x, sticky_shift, precision=int_precision) low_sticky_sign = CI0 if len(x_list) > 1: for x_op in x_list[1:]: sticky = BitLogicOr(sticky, x_op) low_sticky_sign = BitLogicOr( BitLogicXor(CopySign(x_hi), CopySign(x_op)), low_sticky_sign) # does the low sticky (x_list[1:]) differs in signedness from x_hi ? parity_bit = BitLogicAnd(casted_int_x, BitLogicLeftShift(1, delta, precision=int_precision), precision=int_precision) inc_select = LogicalAnd(Equal(sticky, CI0), Equal(parity_bit, CI0)) rounded_value = Select(inc_select, x, pre_rounded_value, precision=precision) # cleaning trailing-bits return TypeCast(BitLogicRightShift(BitLogicLeftShift( TypeCast(rounded_value, precision=int_precision), delta, precision=int_precision), delta, precision=int_precision), precision=precision)
def subnormalize_multi(x_list, factor, precision=None, fma=True): """ x_list is a multi-component number with components ordered from the most to the least siginificant. x_list[0] must be the rounded evaluation of (x_list[0] + x_list[1] + ...) @return the field of x as a floating-point number assuming the exponent of the result is exponent(x) + factor and managing field subnormalization if required """ x_hi = x_list[0] int_precision = precision.get_integer_format() ex = ExponentExtraction(x_hi, precision=int_precision) scaled_ex = Addition(ex, factor, precision=int_precision) CI0 = Constant(0, precision=int_precision) CI1 = Constant(1, precision=int_precision) # difference betwen x's real exponent and the minimal exponent # for a floating of format precision delta = Max(Min(Subtraction(Constant(precision.get_emin_normal(), precision=int_precision), scaled_ex, precision=int_precision), CI0, precision=int_precision), Constant(precision.get_field_size(), precision=int_precision), precision=int_precision) round_factor_exp = Addition(delta, ex, precision=int_precision) round_factor = ExponentInsertion(round_factor_exp, precision=precision) # to force a rounding as if x_hi was of precision p - delta # we use round_factor as follows: # o(o(round_factor + x_hi) - round_factor) if len(x_list) == 2: rounded_x_hi = Subtraction(Add112(round_factor, x_list[0], x_list[1], precision=precision)[0], round_factor, precision=precision) elif len(x_list) == 3: rounded_x_hi = Subtraction(Add113(round_factor, x_list[0], x_list[1], x_list[2], precision=precision)[0], round_factor, precision=precision) else: Log.report(Log.Error, "len of x_list: {} is not supported in subnormalize_multi", len(x_list)) raise NotImplementedError return [rounded_x_hi] + [ Constant(0, precision=precision) for i in range(len(x_list) - 1) ]
def generate_scalar_scheme(self, vx): output_precision = self.precision input_precision = vx.get_precision() bias = -output_precision.get_bias() bound_exp = Max(Min( vx, output_precision.get_emax(), precision=input_precision), output_precision.get_emin_normal(), precision=input_precision) + bias scheme = Return(ExponentInsertion(bound_exp, specifier=ExponentInsertion.NoOffset, precision=self.precision), tag="result", debug=debug_multi) return scheme
def generate_scheme(self): """ main scheme generation """ Log.report(Log.Info, "width parameter is {}".format(self.width)) int_size = 3 frac_size = self.width - int_size input_precision = fixed_point(int_size, frac_size) output_precision = fixed_point(int_size, frac_size) # declaring main input variable var_x = self.implementation.add_input_signal("x", input_precision) var_y = self.implementation.add_input_signal("y", input_precision) var_x.set_attributes(debug=debug_fixed) var_y.set_attributes(debug=debug_fixed) test = (var_x > 1) test.set_attributes(tag="test", debug=debug_std) sub = var_x - var_y c = Constant(0) pre_result_select = Select(c > sub, Select(c < var_y, sub, Select(LogicalAnd( c > var_x, c < var_y, tag="last_lev_cond"), var_x, c, tag="last_lev_sel"), tag="pre_select"), var_y, tag="pre_result_select") pre_result = Max(0, var_x - var_y, tag="pre_result") result = Conversion(Addition(pre_result, pre_result_select, tag="add"), precision=output_precision) self.implementation.add_output_signal("vr_out", result) return [self.implementation]
def piecewise_approximation(function, variable, precision, bound_low=-1.0, bound_high=1.0, num_intervals=16, max_degree=2, error_threshold=S2**-24, odd=False, even=False): """ Generate a piecewise approximation :param function: function to be approximated :type function: SollyaObject :param variable: input variable :type variable: Variable :param precision: variable's format :type precision: ML_Format :param bound_low: lower bound for the approximation interval :param bound_high: upper bound for the approximation interval :param num_intervals: number of sub-interval / sub-division of the main interval :param max_degree: maximum degree for an approximation on any sub-interval :param error_threshold: error bound for an approximation on any sub-interval :return: pair (scheme, error) where scheme is a graph node for an approximation scheme of function evaluated at variable, and error is the maximum approximation error encountered :rtype tuple(ML_Operation, SollyaObject): """ degree_generator = piecewise_approximation_degree_generator( function, bound_low, bound_high, num_intervals=num_intervals, error_threshold=error_threshold, ) degree_list = list(degree_generator) # if max_degree is None then we determine it locally if max_degree is None: max_degree = max(degree_list) # table to store coefficients of the approximation on each segment coeff_table = ML_NewTable( dimensions=[num_intervals, max_degree + 1], storage_precision=precision, tag="coeff_table", const=True # by default all approximation coeff table are const ) error_function = lambda p, f, ai, mod, t: sollya.dirtyinfnorm(p - f, ai) max_approx_error = 0.0 interval_size = (bound_high - bound_low) / num_intervals for i in range(num_intervals): subint_low = bound_low + i * interval_size subint_high = bound_low + (i + 1) * interval_size local_function = function(sollya.x + subint_low) local_interval = Interval(-interval_size, interval_size) local_degree = degree_list[i] if local_degree > max_degree: Log.report( Log.Warning, "local_degree {} exceeds max_degree bound ({}) in piecewise_approximation", local_degree, max_degree) # as max_degree defines the size of the table we can use # it as the degree for each sub-interval polynomial # as there is nothing to gain (yet) by using a smaller polynomial degree = max_degree # min(max_degree, local_degree) if function(subint_low) == 0.0: # if the lower bound is a zero to the function, we # need to force value=0 for the constant coefficient # and extend the approximation interval local_poly_degree_list = list( range(1 if even else 0, degree + 1, 2 if odd or even else 1)) poly_object, approx_error = Polynomial.build_from_approximation_with_error( function(sollya.x) / sollya.x, local_poly_degree_list, [precision] * len(local_poly_degree_list), Interval(-subint_high * 0.95, subint_high), sollya.absolute, error_function=error_function) # multiply by sollya.x poly_object = poly_object.sub_poly(offset=-1) else: try: poly_object, approx_error = Polynomial.build_from_approximation_with_error( local_function, degree, [precision] * (degree + 1), local_interval, sollya.absolute, error_function=error_function) except SollyaError as err: # try to see if function is constant on the interval (possible # failure cause for fpminmax) cst_value = precision.round_sollya_object( function(subint_low), sollya.RN) accuracy = error_threshold diff_with_cst_range = sollya.supnorm(cst_value, local_function, local_interval, sollya.absolute, accuracy) diff_with_cst = sup(abs(diff_with_cst_range)) if diff_with_cst < error_threshold: Log.report(Log.Info, "constant polynomial detected") poly_object = Polynomial([function(subint_low)] + [0] * degree) approx_error = diff_with_cst else: Log.report( Log.error, "degree: {} for index {}, diff_with_cst={} (vs error_threshold={}) ", degree, i, diff_with_cst, error_threshold, error=err) for ci in range(max_degree + 1): if ci in poly_object.coeff_map: coeff_table[i][ci] = poly_object.coeff_map[ci] else: coeff_table[i][ci] = 0.0 if approx_error > error_threshold: Log.report( Log.Warning, "piecewise_approximation on index {} exceeds error threshold: {} > {}", i, approx_error, error_threshold) max_approx_error = max(max_approx_error, abs(approx_error)) # computing offset diff = Subtraction(variable, Constant(bound_low, precision=precision), tag="diff", debug=debug_multi, precision=precision) int_prec = precision.get_integer_format() # delta = bound_high - bound_low delta_ratio = Constant(num_intervals / (bound_high - bound_low), precision=precision) # computing table index # index = nearestint(diff / delta * <num_intervals>) index = Max(0, Min( NearestInteger( Multiplication(diff, delta_ratio, precision=precision), precision=int_prec, ), num_intervals - 1), tag="index", debug=debug_multi, precision=int_prec) poly_var = Subtraction(diff, Multiplication( Conversion(index, precision=precision), Constant(interval_size, precision=precision)), precision=precision, tag="poly_var", debug=debug_multi) # generating indexed polynomial coeffs = [(ci, TableLoad(coeff_table, index, ci)) for ci in range(max_degree + 1)][::-1] poly_scheme = PolynomialSchemeEvaluator.generate_horner_scheme2( coeffs, poly_var, precision, {}, precision) return poly_scheme, max_approx_error
def generate_scheme(self): # We wish to compute vx / vy vx = self.implementation.add_input_variable( "x", self.precision, interval=self.input_intervals[0]) vy = self.implementation.add_input_variable( "y", self.precision, interval=self.input_intervals[1]) # maximum exponent magnitude (to avoid overflow/ underflow during # intermediary computations int_prec = self.precision.get_integer_format() max_exp_mag = Constant(self.precision.get_emax() - 1, precision=int_prec) exact_ex = ExponentExtraction(vx, tag="exact_ex", precision=int_prec, debug=debug_multi) exact_ey = ExponentExtraction(vy, tag="exact_ey", precision=int_prec, debug=debug_multi) ex = Max(Min(exact_ex, max_exp_mag, precision=int_prec), -max_exp_mag, tag="ex", precision=int_prec) ey = Max(Min(exact_ey, max_exp_mag, precision=int_prec), -max_exp_mag, tag="ey", precision=int_prec) Attributes.set_default_rounding_mode(ML_RoundToNearest) Attributes.set_default_silent(True) # computing the inverse square root init_approx = None scaling_factor_x = ExponentInsertion(-ex, tag="sfx_ei", precision=self.precision, debug=debug_multi) scaling_factor_y = ExponentInsertion(-ey, tag="sfy_ei", precision=self.precision, debug=debug_multi) def test_interval_out_of_bound_risk(x_range, y_range): """ Try to determine from x and y's interval if there is a risk of underflow or overflow """ div_range = abs(x_range / y_range) underflow_risk = sollya.inf(div_range) < S2**( self.precision.get_emin_normal() + 2) overflow_risk = sollya.sup(div_range) > S2**( self.precision.get_emax() - 2) return underflow_risk or overflow_risk out_of_bound_risk = (self.input_intervals[0] is None or self.input_intervals[1] is None ) or test_interval_out_of_bound_risk( self.input_intervals[0], self.input_intervals[1]) Log.report(Log.Debug, "out_of_bound_risk: {}".format(out_of_bound_risk)) # scaled version of vx and vy, to avoid overflow and underflow if out_of_bound_risk: scaled_vx = vx * scaling_factor_x scaled_vy = vy * scaling_factor_y scaled_interval = MetaIntervalList( [MetaInterval(Interval(-2, -1)), MetaInterval(Interval(1, 2))]) scaled_vx.set_attributes(tag="scaled_vx", debug=debug_multi, interval=scaled_interval) scaled_vy.set_attributes(tag="scaled_vy", debug=debug_multi, interval=scaled_interval) seed_interval = 1 / scaled_interval print("seed_interval=1/{}={}".format(scaled_interval, seed_interval)) else: scaled_vx = vx scaled_vy = vy seed_interval = 1 / scaled_vy.get_interval() # We need a first approximation to 1 / scaled_vy dummy_seed = ReciprocalSeed(EmptyOperand(precision=self.precision), precision=self.precision) if self.processor.is_supported_operation(dummy_seed, self.language): init_approx = ReciprocalSeed(scaled_vy, precision=self.precision, tag="init_approx", debug=debug_multi) else: # generate tabulated version of seed raise NotImplementedError current_approx_std = init_approx # correctly-rounded inverse computation num_iteration = self.num_iter Attributes.unset_default_rounding_mode() Attributes.unset_default_silent() # check if inputs are zeros x_zero = Test(vx, specifier=Test.IsZero, likely=False, precision=ML_Bool) y_zero = Test(vy, specifier=Test.IsZero, likely=False, precision=ML_Bool) comp_sign = Test(vx, vy, specifier=Test.CompSign, tag="comp_sign", debug=debug_multi) # check if divisor is NaN y_nan = Test(vy, specifier=Test.IsNaN, likely=False, precision=ML_Bool) # check if inputs are signaling NaNs x_snan = Test(vx, specifier=Test.IsSignalingNaN, likely=False, precision=ML_Bool) y_snan = Test(vy, specifier=Test.IsSignalingNaN, likely=False, precision=ML_Bool) # check if inputs are infinities x_inf = Test(vx, specifier=Test.IsInfty, likely=False, tag="x_inf", precision=ML_Bool) y_inf = Test(vy, specifier=Test.IsInfty, likely=False, tag="y_inf", debug=debug_multi, precision=ML_Bool) scheme = None gappa_vx, gappa_vy = None, None # initial reciprocal approximation of 1.0 / scaled_vy inv_iteration_list, recp_approx = compute_reduced_reciprocal( init_approx, scaled_vy, self.num_iter) recp_approx.set_attributes(tag="recp_approx", debug=debug_multi) # approximation of scaled_vx / scaled_vy yerr_last, reduced_div_approx, div_iteration_list = compute_reduced_division( scaled_vx, scaled_vy, recp_approx) eval_error_range, div_eval_error_range = self.solve_eval_error( init_approx, recp_approx, reduced_div_approx, scaled_vx, scaled_vy, inv_iteration_list, div_iteration_list, S2**-7, seed_interval) eval_error = sup(abs(eval_error_range)) recp_interval = 1 / scaled_vy.get_interval() + eval_error_range recp_approx.set_interval(recp_interval) div_interval = scaled_vx.get_interval() / scaled_vy.get_interval( ) + div_eval_error_range reduced_div_approx.set_interval(div_interval) reduced_div_approx.set_tag("reduced_div_approx") if out_of_bound_risk: unscaled_result = scaling_div_result(reduced_div_approx, ex, scaling_factor_y, self.precision) subnormal_result = subnormalize_result(recp_approx, reduced_div_approx, ex, ey, yerr_last, self.precision) else: unscaled_result = reduced_div_approx subnormal_result = reduced_div_approx x_inf_or_nan = Test(vx, specifier=Test.IsInfOrNaN, likely=False) y_inf_or_nan = Test(vy, specifier=Test.IsInfOrNaN, likely=False, tag="y_inf_or_nan", debug=debug_multi) # generate IEEE exception raising only of libm-compliant # mode is enabled enable_raise = self.libm_compliant # managing special cases # x inf and y inf pre_scheme = ConditionBlock( x_inf_or_nan, ConditionBlock( x_inf, ConditionBlock( y_inf_or_nan, Statement( # signaling NaNs raise invalid operation flags ConditionBlock(y_snan, Raise(ML_FPE_Invalid)) if enable_raise else Statement(), Return(FP_QNaN(self.precision)), ), ConditionBlock(comp_sign, Return(FP_MinusInfty(self.precision)), Return(FP_PlusInfty(self.precision)))), Statement( ConditionBlock(x_snan, Raise(ML_FPE_Invalid)) if enable_raise else Statement(), Return(FP_QNaN(self.precision)))), ConditionBlock( x_zero, ConditionBlock( LogicalOr(y_zero, y_nan, precision=ML_Bool), Statement( ConditionBlock(y_snan, Raise(ML_FPE_Invalid)) if enable_raise else Statement(), Return(FP_QNaN(self.precision))), Return(vx)), ConditionBlock( y_inf_or_nan, ConditionBlock( y_inf, Return( Select(comp_sign, FP_MinusZero(self.precision), FP_PlusZero(self.precision))), Statement( ConditionBlock(y_snan, Raise(ML_FPE_Invalid)) if enable_raise else Statement(), Return(FP_QNaN(self.precision)))), ConditionBlock( y_zero, Statement( Raise(ML_FPE_DivideByZero) if enable_raise else Statement(), ConditionBlock( comp_sign, Return(FP_MinusInfty(self.precision)), Return(FP_PlusInfty(self.precision)))), # managing numerical value result cases Statement( recp_approx, reduced_div_approx, ConditionBlock( Test(unscaled_result, specifier=Test.IsSubnormal, likely=False), # result is subnormal Statement( # inexact flag should have been raised when computing yerr_last # ConditionBlock( # Comparison( # yerr_last, 0, # specifier=Comparison.NotEqual, likely=True), # Statement(Raise(ML_FPE_Inexact, ML_FPE_Underflow)) #), Return(subnormal_result), ), # result is normal Statement( # inexact flag should have been raised when computing yerr_last #ConditionBlock( # Comparison( # yerr_last, 0, # specifier=Comparison.NotEqual, likely=True), # Raise(ML_FPE_Inexact) #), Return(unscaled_result))), ))))) # managing rounding mode save and restore # to ensure intermediary computations are performed in round-to-nearest # clearing exception before final computation #rnd_mode = GetRndMode() #scheme = Statement( # rnd_mode, # SetRndMode(ML_RoundToNearest), # yerr_last, # SetRndMode(rnd_mode), # unscaled_result, # ClearException(), # pre_scheme #) scheme = pre_scheme return scheme
def generate_scheme(self): """ main scheme generation """ int_size = 3 frac_size = self.width - int_size input_precision = fixed_point(int_size, frac_size) output_precision = fixed_point(int_size, frac_size) expected_interval = {} # declaring main input variable var_x = self.implementation.add_input_signal("x", input_precision) x_interval = Interval(-10.3,10.7) var_x.set_interval(x_interval) expected_interval[var_x] = x_interval var_y = self.implementation.add_input_signal("y", input_precision) y_interval = Interval(-17.9,17.2) var_y.set_interval(y_interval) expected_interval[var_y] = y_interval var_z = self.implementation.add_input_signal("z", input_precision) z_interval = Interval(-7.3,7.7) var_z.set_interval(z_interval) expected_interval[var_z] = z_interval cst = Constant(42.5, tag = "cst") expected_interval[cst] = Interval(42.5) conv_ceil = Ceil(var_x, tag = "ceil") expected_interval[conv_ceil] = sollya.ceil(x_interval) conv_floor = Floor(var_y, tag = "floor") expected_interval[conv_floor] = sollya.floor(y_interval) mult = var_z * var_x mult.set_tag("mult") mult_interval = z_interval * x_interval expected_interval[mult] = mult_interval large_add = (var_x + var_y) - mult large_add.set_attributes(tag = "large_add") large_add_interval = (x_interval + y_interval) - mult_interval expected_interval[large_add] = large_add_interval var_x_lzc = CountLeadingZeros(var_x, tag="var_x_lzc") expected_interval[var_x_lzc] = Interval(0, input_precision.get_bit_size()) reduced_result = Max(0, Min(large_add, 13)) reduced_result.set_tag("reduced_result") reduced_result_interval = interval_max( Interval(0), interval_min( large_add_interval, Interval(13) ) ) expected_interval[reduced_result] = reduced_result_interval select_result = Select( var_x > var_y, reduced_result, var_z, tag = "select_result" ) select_interval = interval_union(reduced_result_interval, z_interval) expected_interval[select_result] = select_interval # floating-point operation on mantissa and exponents fp_x_range = Interval(-0.01, 100) unbound_fp_var = Variable("fp_x", precision=ML_Binary32, interval=fp_x_range) mant_fp_x = MantissaExtraction(unbound_fp_var, tag="mant_fp_x", precision=ML_Binary32) exp_fp_x = ExponentExtraction(unbound_fp_var, tag="exp_fp_x", precision=ML_Int32) ins_exp_fp_x = ExponentInsertion(exp_fp_x, tag="ins_exp_fp_x", precision=ML_Binary32) expected_interval[unbound_fp_var] = fp_x_range expected_interval[exp_fp_x] = Interval( sollya.floor(sollya.log2(sollya.inf(abs(fp_x_range)))), sollya.floor(sollya.log2(sollya.sup(abs(fp_x_range)))) ) expected_interval[mant_fp_x] = Interval(1, 2) expected_interval[ins_exp_fp_x] = Interval( S2**sollya.inf(expected_interval[exp_fp_x]), S2**sollya.sup(expected_interval[exp_fp_x]) ) # checking interval evaluation for var in [var_x_lzc, exp_fp_x, unbound_fp_var, mant_fp_x, ins_exp_fp_x, cst, var_x, var_y, mult, large_add, reduced_result, select_result, conv_ceil, conv_floor]: interval = evaluate_range(var) expected = expected_interval[var] print("{}: {}".format(var.get_tag(), interval)) print(" vs expected {}".format(expected)) assert not interval is None assert interval == expected return [self.implementation]
def generate_scheme(self): """ main scheme generation """ int_size = 3 frac_size = self.width - int_size input_precision = fixed_point(int_size, frac_size) output_precision = fixed_point(int_size, frac_size) expected_interval = {} # declaring main input variable var_x = self.implementation.add_input_signal("x", input_precision) x_interval = Interval(-10.3, 10.7) var_x.set_interval(x_interval) expected_interval[var_x] = x_interval var_y = self.implementation.add_input_signal("y", input_precision) y_interval = Interval(-17.9, 17.2) var_y.set_interval(y_interval) expected_interval[var_y] = y_interval var_z = self.implementation.add_input_signal("z", input_precision) z_interval = Interval(-7.3, 7.7) var_z.set_interval(z_interval) expected_interval[var_z] = z_interval cst = Constant(42.5, tag="cst") expected_interval[cst] = Interval(42.5) conv_ceil = Ceil(var_x, tag="ceil") expected_interval[conv_ceil] = sollya.ceil(x_interval) conv_floor = Floor(var_y, tag="floor") expected_interval[conv_floor] = sollya.floor(y_interval) mult = var_z * var_x mult.set_tag("mult") mult_interval = z_interval * x_interval expected_interval[mult] = mult_interval large_add = (var_x + var_y) - mult large_add.set_attributes(tag="large_add") large_add_interval = (x_interval + y_interval) - mult_interval expected_interval[large_add] = large_add_interval reduced_result = Max(0, Min(large_add, 13)) reduced_result.set_tag("reduced_result") reduced_result_interval = interval_max( Interval(0), interval_min(large_add_interval, Interval(13))) expected_interval[reduced_result] = reduced_result_interval select_result = Select(var_x > var_y, reduced_result, var_z, tag="select_result") select_interval = interval_union(reduced_result_interval, z_interval) expected_interval[select_result] = select_interval # checking interval evaluation for var in [ cst, var_x, var_y, mult, large_add, reduced_result, select_result, conv_ceil, conv_floor ]: interval = evaluate_range(var) expected = expected_interval[var] print("{}: {} vs expected {}".format(var.get_tag(), interval, expected)) assert not interval is None assert interval == expected return [self.implementation]
def piecewise_approximation(function, variable, precision, bound_low=-1.0, bound_high=1.0, num_intervals=16, max_degree=2, error_threshold=sollya.S2**-24): """ To be documented """ # table to store coefficients of the approximation on each segment coeff_table = ML_NewTable(dimensions=[num_intervals, max_degree + 1], storage_precision=precision, tag="coeff_table") error_function = lambda p, f, ai, mod, t: sollya.dirtyinfnorm(p - f, ai) max_approx_error = 0.0 interval_size = (bound_high - bound_low) / num_intervals for i in range(num_intervals): subint_low = bound_low + i * interval_size subint_high = bound_low + (i + 1) * interval_size #local_function = function(sollya.x) #local_interval = Interval(subint_low, subint_high) local_function = function(sollya.x + subint_low) local_interval = Interval(-interval_size, interval_size) local_degree = sollya.guessdegree(local_function, local_interval, error_threshold) degree = min(max_degree, local_degree) if function(subint_low) == 0.0: # if the lower bound is a zero to the function, we # need to force value=0 for the constant coefficient # and extend the approximation interval degree_list = range(1, degree + 1) poly_object, approx_error = Polynomial.build_from_approximation_with_error( function(sollya.x), degree_list, [precision] * len(degree_list), Interval(-subint_high, subint_high), sollya.absolute, error_function=error_function) else: try: poly_object, approx_error = Polynomial.build_from_approximation_with_error( local_function, degree, [precision] * (degree + 1), local_interval, sollya.absolute, error_function=error_function) except SollyaError as err: print("degree: {}".format(degree)) raise err for ci in range(degree + 1): if ci in poly_object.coeff_map: coeff_table[i][ci] = poly_object.coeff_map[ci] else: coeff_table[i][ci] = 0.0 max_approx_error = max(max_approx_error, abs(approx_error)) # computing offset diff = Subtraction(variable, Constant(bound_low, precision=precision), tag="diff", precision=precision) # delta = bound_high - bound_low delta_ratio = Constant(num_intervals / (bound_high - bound_low), precision=precision) # computing table index # index = nearestint(diff / delta * <num_intervals>) index = Max(0, Min( NearestInteger(Multiplication(diff, delta_ratio, precision=precision), precision=ML_Int32), num_intervals - 1), tag="index", debug=True, precision=ML_Int32) poly_var = Subtraction(diff, Multiplication( Conversion(index, precision=precision), Constant(interval_size, precision=precision)), precision=precision, tag="poly_var", debug=True) # generating indexed polynomial coeffs = [(ci, TableLoad(coeff_table, index, ci)) for ci in range(degree + 1)][::-1] poly_scheme = PolynomialSchemeEvaluator.generate_horner_scheme2( coeffs, poly_var, precision, {}, precision) return poly_scheme, max_approx_error
def generate_scheme(self): """ main scheme generation """ int_size = 3 frac_size = self.width - int_size input_precision = hdl_precision_parser("FU%d.%d" % (int_size, frac_size)) output_precision = hdl_precision_parser("FS%d.%d" % (int_size, frac_size)) # declaring main input variable var_x = self.implementation.add_input_signal("x", input_precision) var_y = self.implementation.add_input_signal("y", input_precision) var_z = self.implementation.add_input_signal("z", input_precision) abstract_formulae = var_x + var_y * var_z + 7 round_bit = BitSelection( abstract_formulae, FixedPointPosition(abstract_formulae, 0, align=FixedPointPosition.FromPointToLSB), ) msb_bit = BitSelection( abstract_formulae, FixedPointPosition(abstract_formulae, 0, align=FixedPointPosition.FromMSBToLSB)) lsb_bit = BitSelection( abstract_formulae, FixedPointPosition(abstract_formulae, 0, align=FixedPointPosition.FromLSBToLSB)) # testing parameterized fixed-point format dynamic_format = lazy_fixed_point( FixedPointPosition(abstract_formulae, 0, tag="lazy_up", align=FixedPointPosition.FromPointToMSB) - Max( 0, FixedPointPosition(abstract_formulae, 2, align=FixedPointPosition.FromLSBToLSB) - 2), FixedPointPosition( abstract_formulae, 0, align=FixedPointPosition.FromPointToLSB) + Min( FixedPointPosition(abstract_formulae, 2, align=FixedPointPosition.FromPointToLSB) - 0, FixedPointPosition( abstract_formulae, 2, align=FixedPointPosition.FromPointToLSB, ) - 0), ) result = Constant(3, precision=dynamic_format) self.implementation.add_output_signal("round", round_bit) self.implementation.add_output_signal("msb", msb_bit) self.implementation.add_output_signal("lsb", lsb_bit) self.implementation.add_output_signal("result", result) return [self.implementation]