def test_no_repeats(self): a = Argument("a") b = Argument("b") exp = a + b - a assert exp.get_all_argument_names() == ["a", "b"]
def test_arguments(self): assert Argument("foo").generate_code() == ("def f(foo):\n" " return foo\n") # Arguments should be in alphabetical order foo_bar = Argument("foo") + Argument("bar") assert foo_bar.generate_code() == ("def f(bar, foo):\n" " return (foo + bar)\n")
def test_ordered(self): a = Argument("a") b = Argument("b") exp = a + b assert exp.get_all_argument_names() == ["a", "b"] exp = b + a assert exp.get_all_argument_names() == ["a", "b"]
def test_binary_operator_uses_pyexp_passed_in(self): lhs = Argument("lhs") rhs = Argument("rhs") exp = PyExp._binary_operator(lhs, rhs, "*") assert isinstance(exp, BinaryOperator) assert exp._lhs == lhs assert exp._rhs == rhs assert exp._operator == "*"
def test_deeply_nested_expression(): a = Argument("a") nest_depth = 1000 for _ in range(nest_depth): a += 1 f = a.make_function() assert f(10) == 10 + nest_depth
def test_make_variable_coeff_arrays(): coeff_arrays = make_variable_coeff_arrays(2, 0, Argument("foobar")) assert isinstance(coeff_arrays[1]["HL"][2, 3], PyExp) assert coeff_arrays[1]["HL"][2, 3] == Argument("foobar")[1]["HL"][2, 3] # Check default argument works too since it is a somewhat exciting type... assert make_variable_coeff_arrays( 2, 0)[1]["HL"][2, 3] == Argument("coeffs")[1]["HL"][2, 3]
def test_make_function(): a = Argument("a") b = Argument("b") c = Argument("c") abc = (a + b) * c f = abc.make_function() assert f(a=1, b=2, c=10) == (1 + 2) * 10
def test_variable_array(): a = VariableArray(3, Argument("arg")) assert a.period == (1, 1, 1) assert a.nop is False assert a.relative_step_size_to(a) == (1, 1, 1) assert a.relative_step_size_to(VariableArray(2, Argument("arg"))) is None assert a[0, 0, 0] == Subscript(Argument("arg"), Constant((0, 0, 0))) assert a[1, 2, 3] == Subscript(Argument("arg"), Constant((1, 2, 3)))
def test_swap(self): # Check variable swapping is supported (i.e. items in before and after # are same) a = Argument("a") b = Argument("b") a_sub_b = a - b b_sub_a = a_sub_b.subs({a: b, b: a}) assert str(b_sub_a) == str(b - a)
def test_only_used_once(self): a = Argument("a") b = Argument("b") c = Argument("c") a_plus_b = a + b a_plus_b_plus_c = a_plus_b + c a_plus_b_plus_c._auto_inline() assert a_plus_b_plus_c._inline is True assert a_plus_b._inline is True
def test_used_multiple_times(self): a = Argument("a") b = Argument("b") c = Argument("c") a_plus_b = a + b twice_a_plus_b = a_plus_b + a_plus_b twice_a_plus_b._auto_inline() assert twice_a_plus_b._inline is True assert a_plus_b._inline is False
def make_variable_coeff_arrays(dwt_depth, dwt_depth_ho, exp=Argument("coeffs")): r""" Create a set of :py:class:`~vc2_bit_widths.infinite_arrays.SymbolArray`\ s representing transform coefficient values, as expected by :py:func:`synthesis_transform`. Returns ======= coeff_arrays : {level: {orientation: :py:class:`~vc2_bit_widths.infinite_arrays.VariableArray`, ...}, ...} The transform coefficient values. These dictionaries are indexed the same way as 'coeff_data' in the idwt pseudocode function in (15.4.1) in the VC-2 specification. The expressions within the :py:class:`~vc2_bit_widths.infinite_arrays.VariableArray`\ s will be indexed as follows:: >>> from vc2_bit_widths.pyexp import Argument >>> coeffs_arg = Argument("coeffs_arg") >>> coeff_arrays = make_variable_coeff_arrays(3, 0, coeffs_arg) >>> coeff_arrays[2]["LH"][3, 4] == coeffs_arg[2]["LH"][3, 4] True """ def make_array(level, orient): return VariableArray(2, exp[level][orient]) return make_coeff_arrays(dwt_depth, dwt_depth_ho, make_array)
def test_ensure_py_exp(): # Passes through PyExp types unchanged foo = Argument("foo") assert ensure_py_exp(foo) is foo # Wraps other values in Constant assert ensure_py_exp(123) == Constant(123)
def test_subs_in_reused_binary_operator(self): # Check if operands to a re-used binary operator are changed, the new # expression still re-uses the updated binary operator. a = Argument("a") b = Argument("b") c = Argument("c") d = Argument("d") a_plus_b = a + b a_plus_b_squared = a_plus_b * a_plus_b # Sanity check assert a_plus_b_squared.lhs is a_plus_b_squared.rhs c_plus_d_squared = a_plus_b_squared.subs({a: c, b: d}) assert c_plus_d_squared.lhs is c_plus_d_squared.rhs
def test_subscript(self): a = Argument("a") c = Constant(123) s = a[c] assert isinstance(s, Subscript) assert s.exp is a assert s.key is c
def test_nested_out_of_lineable_values(self): a = Argument("a") b = a + 1 # Depth 2 -> 0 (de-inlined) c = b + 1 # Depth 1 d = c + 1 # Depth 2 -> 0 (de-inlined) e = d + 1 # Depth 1 f = e + 1 # Depth 0 f._auto_inline(2) assert not b._inline assert c._inline assert not d._inline assert e._inline assert f._inline
def test_binary_operators(self, operator): a = Argument("a") b = Argument("b") # Native operation exp = eval("a {} b".format(operator), {}, {"a": a, "b": b}) assert isinstance(exp, BinaryOperator) assert exp._lhs == a assert exp._rhs == b assert exp._operator == operator # RHS is not PyExp exp = eval("a {} 123".format(operator), {}, {"a": a}) assert isinstance(exp, BinaryOperator) assert exp._lhs == a assert exp._rhs == Constant(123) assert exp._operator == operator # Reverse-operator methods (and LHS is not PyExp) exp = eval("123 {} a".format(operator), {}, {"a": a}) assert isinstance(exp, BinaryOperator) assert exp._lhs == Constant(123) assert exp._rhs == a assert exp._operator == operator
def test_get_transform_coeffs_used_by_synthesis_exp(): a = Argument("a") exp = ( a[0]["L"][(1, 2)] + a[1]["H"][(2, 3)] + a[2]["LH"][(3, 4)] + 123 ) assert set(get_transform_coeffs_used_by_synthesis_exp(exp)) == set([ (0, "L", 1, 2), (1, "H", 2, 3), (2, "LH", 3, 4), ])
def test_subs(self, exp, key): s = Subscript(exp, key) # Substitute whole subscript assert s.subs({exp[key]: Argument("bar")}) == Argument("bar") # Substitute expression assert s.subs({exp: Argument("bar")}) == Argument("bar")[123] # Substitute key assert s.subs({key: Constant(321)}) == exp[321] # Substitute nothing relevant assert s.subs({Argument("bar"): Argument("xxx")}) is s
def exp_coeff_nested_dicts_to_list(exp): """ Transform a synthesis transform describing :py:class:`~vc2_bit_widths.pyexp.PyExp` such that instead of taking a nested dictionary of coefficient value arrays it takes a single list of coefficient values. This transformation can speed up the execution of the generated function by around 3x while also eliminating the need to construct nested dictionaries of values as arguments (also a substantial performance saving). Parameters ========== exp : :py:class:`~vc2_bit_widths.pyexp.PyExp` An expression defined in terms of a single :py:class:`~vc2_bit_widths.pyexp.Argument` which is always used subscripted in the form ``arg[level][orient][x, y]``. Returns ======= exp : :py:class:`~vc2_bit_widths.pyexp.PyExp` A modified expression which takes a single :py:class:`list` as argument. The values in this list should correspond to the transform coefficients enumerated in ``transform_coeffs_used`` transform_coeffs_used : [(level, orient, x, y), ...] The transform coefficients expected by the returned expression, in the order they're expected. """ transform_coeffs_used = get_transform_coeffs_used_by_synthesis_exp(exp) # Find the Argument in the expression all_argument_names = exp.get_all_argument_names() assert len(all_argument_names) == 1 argument = Argument(all_argument_names[0]) new_exp = exp.subs({ argument[level][orient][x, y]: argument[i] for i, (level, orient, x, y) in enumerate(transform_coeffs_used) }) return (new_exp, transform_coeffs_used)
def test_subs(self, lhs, rhs): b = lhs + rhs # Substitute whole subscript: NB: same object must be used in this case assert b.subs({b: Argument("bar")}) == Argument("bar") # NB: in the following we must comprae string for true equality since # BinaryOperators are compared by identity # Substitute LHS assert repr(b.subs({lhs: Argument("foo")})) == repr(Argument("foo") + rhs) # Substitute RHS assert str(b.subs({rhs: Argument("foo")})) == str(lhs + Argument("foo")) # Substitution which makes operation a no-op should not return a # BinaryOperator assert b.subs({rhs: Constant(0)}) == lhs # Substitute nothing relevant assert b.subs({Argument("bar"): Argument("xxx")}) is b
def test_exp_coeff_nested_dicts_to_list(): a = Argument("a") dict_exp = ( a[0]["L"][(1, 2)] - a[1]["H"][(2, 3)] + 123 ) list_exp, transform_coeffs_used = exp_coeff_nested_dicts_to_list(dict_exp) coeffs_dict = { 0: {"L": {(1, 2): 100}}, 1: {"H": {(2, 3): 1000}}, } coeffs_list = [ coeffs_dict[level][orient][x, y] for level, orient, x, y in transform_coeffs_used ] dict_f = dict_exp.make_function() list_f = list_exp.make_function() assert dict_f(a=coeffs_dict) == list_f(a=coeffs_list)
def test_eq(self, exp, key): assert Subscript(exp, key) == Subscript(exp, key) assert Subscript(exp, key) != Subscript(Argument("bar"), key) assert Subscript(exp, key) != Subscript(exp, Constant(321))
def test_hash(self, exp, key): assert hash(Subscript(exp, key)) == hash(Subscript(exp, key)) assert hash(Subscript(exp, key)) != hash( Subscript(Argument("bar"), key)) assert hash(Subscript(exp, key)) != hash(Subscript(exp, Constant(321)))
def exp(self): return Argument("foo")
def test_subs(self): a = Argument("foo") assert a.subs({Argument("foo"): Argument("bar")}) == Argument("bar") assert a.subs({Argument("bar"): Argument("xxx")}) is a
def test_repr(self): assert repr(Argument("foo")) == "Argument('foo')"
def test_code(self): a = Argument("foo") assert list(a.get_dependencies()) == [] assert list(a.get_argument_names()) == ["foo"] assert a.get_definitions() == "" assert a.get_expression() == "foo"
def test_hash(self): assert hash(Argument("foo")) == hash(Argument("foo")) assert hash(Argument("foo")) != hash(Argument("bar"))
def test_eq(self): assert Argument("foo") == Argument("foo") assert Argument("foo") != Argument("bar") assert Argument("foo") != Constant("foo")