def period_empirically_correct(a): """ Empirically verify the period a particular array claims to have. Checks that expected repeats are literal repeated values (minus differing error terms). """ last_values = None # Get the hyper-cube block of values which sample the complete period # of the array at various period-multiple offsets for offset_steps in combinations_with_replacement([-1, 0, 1, 2], a.ndim): offset = tuple(step * period for step, period in zip(offset_steps, a.period)) values = [ # Since error terms will always be different, consider the # lower-bound case arbitrarily in order to make a fair comparison. affine_lower_bound(a[tuple(c + o for c, o in zip(coord, offset))]) for coord in product(*(range(d) for d in a.period)) ] print(values) # Every set of values should be identical if last_values is not None and values != last_values: return False last_values = values return True
def test_correctness(self, stage): # This test checks that the filter implemented is equivalent to what # the VC-2 pseudocode would do a = SymbolArray(2, "a") l = LiftedArray(a, stage, 0) # Run the pseudocode against a random input rand = random.Random(0) input_array = [rand.randint(0, 10000) for _ in range(20)] pseudocode_output_array = input_array[:] lift = SYNTHESIS_LIFTING_FUNCTION_TYPES[stage.lift_type] lift(pseudocode_output_array, stage.L, stage.D, stage.taps, stage.S) # Check that the symbolic version gets the same answers (modulo # rounding errors). Check at output positions which are not affected by # rounding errors. for index in [10, 11]: pseudocode_output = pseudocode_output_array[index] # Substitute in the random inputs into symbolic answer output = l[index, 123].subs({("a", i, 123): value for i, value in enumerate(input_array)}) lower_bound = affine_lower_bound(output) upper_bound = affine_upper_bound(output) assert (lower_bound <= pseudocode_output <= upper_bound)
def test_shifting(self): a = SymbolArray(3, "foo") sa = RightShiftedArray(a, 3) v = a[1, 2, 3] sv = sa[1, 2, 3] assert affine_lower_bound(sv) == v / 8 - Fraction(1, 2) assert affine_upper_bound(sv) == v / 8 + Fraction(1, 2)
def synthesis_filter_bounds(expression): """ Find the lower- and upper-bound reachable in a :py:class:`~vc2_bit_widths.linexp.LinExp` describing a synthesis filter. The filter expression must contain only affine error symbols (:py:class:`~vc2_bit_widths.linexp.AAError`) and symbols of the form ``((_, level, orient), x, y)`` representing transform coefficients. Parameters ========== expression : :py:class:`~vc2_bit_widths.linexp.LinExp` Returns ======= (lower_bound, upper_bound) : :py:class:`~vc2_bit_widths.linexp.LinExp` Lower and upper bounds for the signal level in terms of the symbols of the form ``LinExp("signal_LEVEL_ORIENT_min")`` and ``LinExp("signal_LEVEL_ORIENT_max")``, representing the minimum and maximum signal levels for transform coefficients in level ``LEVEL`` and orientation ``ORIENT`` respectively. """ # Replace all transform coefficients with affine error terms scaled to # appropriate ranges expression = LinExp(expression) lower_bound = affine_lower_bound( expression.subs({ sym: ("coeff_{}_{}_min" if coeff > 0 else "coeff_{}_{}_max").format( sym[0][1], sym[0][2], ) for sym, coeff in expression if sym is not None and not isinstance(sym, AAError) })) upper_bound = affine_upper_bound( expression.subs({ sym: ("coeff_{}_{}_min" if coeff < 0 else "coeff_{}_{}_max").format( sym[0][1], sym[0][2], ) for sym, coeff in expression if sym is not None and not isinstance(sym, AAError) })) return (lower_bound, upper_bound)
def analysis_filter_bounds(expression): """ Find the lower- and upper-bound reachable in a :py:class:`~vc2_bit_widths.linexp.LinExp` describing an analysis filter. The filter expression must consist of only affine error symbols (:py:class:`~vc2_bit_widths.linexp.AAError`) and symbols of the form ``(_, x, y)`` representing pixel values in an input picture. Parameters ========== expression : :py:class:`~vc2_bit_widths.linexp.LinExp` Returns ======= lower_bound : :py:class:`~vc2_bit_widths.linexp.LinExp` upper_bound : :py:class:`~vc2_bit_widths.linexp.LinExp` Algebraic expressions for the lower and upper bounds for the signal level. These expressions are given in terms of the symbols ``LinExp("signal_min")`` and ``LinExp("signal_max")``, which represent the minimum and maximum picture signal levels respectively. """ signal_min = LinExp("signal_min") signal_max = LinExp("signal_max") expression = LinExp(expression) lower_bound = affine_lower_bound( expression.subs({ sym: signal_min if coeff > 0 else signal_max for sym, coeff in expression if sym is not None and not isinstance(sym, AAError) })) upper_bound = affine_upper_bound( expression.subs({ sym: signal_min if coeff < 0 else signal_max for sym, coeff in expression if sym is not None and not isinstance(sym, AAError) })) return (lower_bound, upper_bound)
def test_error_in_range(): a = affine_error_with_range(-10, 123) assert affine_lower_bound(a) == -10 assert affine_upper_bound(a) == 123
def test_affine_bounds(expr_in, lower, upper): assert affine_lower_bound(expr_in) == lower assert affine_upper_bound(expr_in) == upper
def test_floordiv(self): a = LinExp("a") a_over_3 = a // 3 assert affine_lower_bound(a_over_3) == (a / 3) - 1 assert affine_upper_bound(a_over_3) == a / 3