Exemplo n.º 1
0
    def test_integration(self):
        # A simple test showing most of the moving parts used in a way they
        # might be in a video filter...

        a = LinExp("a")
        b = LinExp("b")

        ab = a + b
        assert str(ab) == "a + b"

        ab3 = 3 * ab
        assert str(ab3) == "3*a + 3*b"

        ab2 = ab3 * Fraction(2, 3)
        assert str(ab2) == "2*a + 2*b"

        b2_1 = 2 * b - 1
        assert str(b2_1) == "2*b + -1"

        a2_1 = ab2 - b2_1
        assert str(a2_1) == "2*a + 1"

        a_05 = a2_1 / 2
        assert str(a_05) == "a + 1/2"

        two_and_a_half = a_05.subs({"a": 2})
        assert str(two_and_a_half) == "5/2"

        three = (3 * a_05) / a_05
        assert str(three) == "3"
Exemplo n.º 2
0
    def test_constructor_linexp(self):
        v1 = LinExp({"a": 1, "b": 2})

        v2 = LinExp(v1)

        # Should pass through original object without creating a new value
        assert v1 is v2
Exemplo n.º 3
0
 def test_lt_free_symbols_dont_cancel(self):
     with pytest.raises(TypeError):
         LinExp({"a": 1}) < LinExp({"a": 2})
     with pytest.raises(TypeError):
         123 < LinExp({"a": 2})
     with pytest.raises(TypeError):
         LinExp({"a": 1}) < 123
Exemplo n.º 4
0
 def test_div(self):
     assert LinExp({
         "a": 10,
         None: 100
     }) / LinExp(2) == LinExp({
         "a": 5,
         None: 50
     })
     # rrealdiv/rdiv
     assert 0 / LinExp({"a": 10, None: 100}) == LinExp(0)
Exemplo n.º 5
0
    def test_new_affine_error_symbol(self):
        e1 = LinExp.new_affine_error_symbol()
        e2 = LinExp.new_affine_error_symbol()

        assert e1.is_symbol
        assert isinstance(e1.symbol, AAError)
        assert e2.is_symbol
        assert isinstance(e2.symbol, AAError)

        assert e1 != e2
Exemplo n.º 6
0
 def test_mul(self):
     assert LinExp({
         "a": 10,
         None: 100
     }) * LinExp(3) == LinExp({
         "a": 30,
         None: 300
     })
     # rmul
     assert 3 * LinExp({"a": 10, None: 100}) == LinExp({"a": 30, None: 300})
Exemplo n.º 7
0
def test_symbol_array():
    a = SymbolArray(3, "foo")

    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(SymbolArray(2, "bar")) is None

    assert a[0, 0, 0] == LinExp(("foo", 0, 0, 0))

    assert a[1, 2, 3] == LinExp(("foo", 1, 2, 3))
Exemplo n.º 8
0
    def test_subsampling(self):
        a = SymbolArray(3, "v")
        s = SubsampledArray(a, (1, 2, 3), (0, 10, 20))

        assert s[0, 0, 0] == LinExp(("v", 0, 10, 20))

        assert s[1, 0, 0] == LinExp(("v", 1, 10, 20))
        assert s[0, 1, 0] == LinExp(("v", 0, 12, 20))
        assert s[0, 0, 1] == LinExp(("v", 0, 10, 23))

        assert s[2, 2, 2] == LinExp(("v", 2, 14, 26))
Exemplo n.º 9
0
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)
Exemplo n.º 10
0
    def test_eq_constants(self):
        assert LinExp(123) == LinExp(123)
        assert LinExp(123) == 123
        assert 123 == LinExp(123)

        assert LinExp(123) != LinExp(321)
        assert LinExp(123) != 321
        assert 321 != LinExp(123)
Exemplo n.º 11
0
    def test_getitem(self):
        v = LinExp({"a": 123, "b": 321, None: 111})

        assert v["a"] == 123
        assert v["b"] == 321
        assert v[None] == 111
        assert v["xxx"] == 0
Exemplo n.º 12
0
    def test_contains(self):
        v = LinExp({"a": 123, "b": 321, None: 111})

        assert ("a" in v) is True
        assert ("b" in v) is True
        assert (None in v) is True
        assert ("xxx" in v) is False
Exemplo n.º 13
0
def test_find_analysis_filter_bounds():
    expr = (1 * LinExp(("pixel", 0, 0)) + 2 * LinExp(
        ("pixel", 1, 0)) + -4 * LinExp(
            ("pixel", 2, 0)) + 10 * LinExp.new_affine_error_symbol() + 1)
    lower_bound, upper_bound = analysis_filter_bounds(expr)
    assert lower_bound == 3 * LinExp("signal_min") + -4 * LinExp(
        "signal_max") - 10 + 1
    assert upper_bound == -4 * LinExp("signal_min") + 3 * LinExp(
        "signal_max") + 10 + 1
Exemplo n.º 14
0
def deserialise_linexp(json):
    """
    Inverse of :py:func:`serialise_linexp`.
    """
    return LinExp({
        d["symbol"]: Fraction(int(d["numer"]), int(d["denom"]))
        for d in json
    })
Exemplo n.º 15
0
    def test_hash(self):
        # Hashses should be order insensitive
        assert hash(LinExp(OrderedDict([
            ("a", 123),
            ("b", 321),
            ("c", 111),
        ]))) == hash(
            LinExp(OrderedDict([
                ("c", 111),
                ("b", 321),
                ("a", 123),
            ])))

        # Hashes of LinExp constants should match the raw constant
        assert hash(LinExp(123)) == hash(123)
        assert hash(LinExp(Fraction(1, 3))) == hash(Fraction(1, 3))

        # Hashes of symbols should match the raw symbols
        assert hash(LinExp("a")) == hash("a")

        # Hashes should produce different values for different contents... Note
        # that this test is not strictly guaranteed to pass in theory, though
        # it practice it is almost certain to do so. Should it start failing,
        # think very hard!!
        assert hash(LinExp({"a": 123, "b": 321})) != hash(LinExp({"a": 123}))
Exemplo n.º 16
0
    def test_pow(self):
        assert LinExp(2)**LinExp(3) == LinExp(8)

        # Modulo never supported
        with pytest.raises(TypeError):
            LinExp(2)**LinExp(3) % 5

        # rpow
        assert 2**LinExp(3) == LinExp(8)
Exemplo n.º 17
0
def serialise_signal_bounds(signal_bounds):
    """
    Convert a dictionary of analysis or synthesis signal bounds expressions
    into a JSON-serialisable form.
    
    See :py:func:`serialise_linexp` for details of the ``"lower_bound"`` and
    ``"upper_bound"`` fields.
    
    For example::
        >>> before = {
        ...     (1, "LH", 2, 3): (
        ...         LinExp("foo")/2 + 1,
        ...         LinExp("bar")/4 + 2,
        ...     ),
        ...     ...
        ... }
        >>> serialise_signal_bounds(before)
        [
            {
                "level": 1,
                "array_name": "LH",
                "phase": [2, 3],
                "lower_bound": [
                    {"symbol": "foo", "numer": "1", "denom": "2"},
                    {"symbol": None, "numer": "1", "denom": "1"},
                ],
                "upper_bound": [
                    {"symbol": "bar", "numer": "1", "denom": "4"},
                    {"symbol": None, "numer": "2", "denom": "1"},
                ],
            },
            ...
        ]
    """
    return serialise_intermediate_value_dictionary(
        OrderedDict((
            key,
            {
                "lower_bound": serialise_linexp(LinExp(lower_bound)),
                "upper_bound": serialise_linexp(LinExp(upper_bound)),
            },
        ) for key, (lower_bound, upper_bound) in signal_bounds.items()))
Exemplo n.º 18
0
    def test_number_type_casts(self):
        v = LinExp(1.5)

        assert isinstance(complex(v), complex)
        assert complex(v) == 1.5 + 0j

        assert isinstance(float(v), float)
        assert float(v) == 1.5

        assert isinstance(int(v), int)
        assert int(v) == 1
Exemplo n.º 19
0
    def test_rshift_operator(self, a, b, exp_result):
        actual_result = LinExp._rshift_operator(a, b)

        # Make error symbols match
        if exp_result is not NotImplemented:
            actual_error = actual_result - strip_affine_errors(actual_result)
            exp_error = exp_result - strip_affine_errors(exp_result)
            actual_result = actual_result.subs(
                {next(actual_error.symbols()): next(exp_error.symbols())})

        assert actual_result == exp_result
Exemplo n.º 20
0
 def test_constructor_dictionary(self):
     # Zero coefficients should be removed
     assert LinExp({
         "a": 100,
         "b": 200,
         "c": 0,
         None: 123
     })._coeffs == {
         "a": 100,
         "b": 200,
         None: 123,
     }
Exemplo n.º 21
0
 def test_add(self):
     assert (LinExp({
         "a": 10,
         "b": 20
     }) + LinExp({
         "b": 30,
         "c": 40
     }) == LinExp({
         "a": 10,
         "b": 50,
         "c": 40
     }))
     # radd
     assert (10 + LinExp({
         "a": 20,
         "b": 30
     }) == LinExp({
         None: 10,
         "a": 20,
         "b": 30
     }))
Exemplo n.º 22
0
def test_evaluate_synthesis_filter_bounds(dc_band_name):
    expr = (LinExp((("coeff", 0, dc_band_name), 1, 2)) + LinExp(
        (("coeff", 2, "HH"), 3, 4)) + 10000)
    lower_bound_exp, upper_bound_exp = synthesis_filter_bounds(expr)

    coeff_bounds = {
        (0, dc_band_name): (-100, 200),
        (2, "HH"): (-1000, 2000),
    }

    lower_bound, upper_bound = evaluate_synthesis_filter_bounds(
        lower_bound_exp,
        upper_bound_exp,
        coeff_bounds,
    )

    assert isinstance(lower_bound, int)
    assert isinstance(upper_bound, int)

    assert lower_bound == 10000 - 100 - 1000
    assert upper_bound == 10000 + 200 + 2000
Exemplo n.º 23
0
 def test_sub(self):
     assert (LinExp({
         "a": 10,
         "b": 20
     }) - LinExp({
         "b": 30,
         "c": 40
     }) == LinExp({
         "a": 10,
         "b": -10,
         "c": -40
     }))
     # rsub
     assert (10 - LinExp({
         "a": 20,
         "b": 30
     }) == LinExp({
         None: 10,
         "a": -20,
         "b": -30
     }))
Exemplo n.º 24
0
def test_serialise_signal_bounds():
    before = OrderedDict([
        ((1, "LH", 2, 3), (
            LinExp("foo")/2,
            LinExp("bar")/4,
        )),
        ((2, "HL", 3, 2), (
            LinExp("qux")/8,
            LinExp("quo")/16,
        )),
    ])
    after = serialise_signal_bounds(before)
    after = json_roundtrip(after)
    
    assert after == [
        {
            "level": 1,
            "array_name": "LH",
            "phase": [2, 3],
            "lower_bound": [
                {"symbol": "foo", "numer": "1", "denom": "2"},
            ],
            "upper_bound": [
                {"symbol": "bar", "numer": "1", "denom": "4"},
            ],
        },
        {
            "level": 2,
            "array_name": "HL",
            "phase": [3, 2],
            "lower_bound": [
                {"symbol": "qux", "numer": "1", "denom": "8"},
            ],
            "upper_bound": [
                {"symbol": "quo", "numer": "1", "denom": "16"},
            ],
        },
    ]
    
    assert deserialise_signal_bounds(after) == before
Exemplo n.º 25
0
def test_serialise_linexp():
    before = LinExp("foo")/2 + 1
    after = serialise_linexp(before)
    after = json_roundtrip(after)
    
    exp = [
        {"symbol": "foo", "numer": "1", "denom": "2"},
        {"symbol": None, "numer": "1", "denom": "1"},
    ]
    
    assert after == exp or after == exp[::-1]
    
    assert deserialise_linexp(after) == before
Exemplo n.º 26
0
def test_find_synthesis_filter_bounds():
    coeff_arrays = make_symbol_coeff_arrays(1, 0)

    expr = (1 * coeff_arrays[0]["LL"][0, 0] + 2 * coeff_arrays[0]["LL"][1, 0] +
            -4 * coeff_arrays[0]["LL"][2, 0] +
            10 * coeff_arrays[1]["HH"][0, 0] +
            20 * coeff_arrays[1]["HH"][1, 0] +
            -40 * coeff_arrays[1]["HH"][2, 0] +
            100 * LinExp.new_affine_error_symbol() + 1)

    lower_bound, upper_bound = synthesis_filter_bounds(expr)
    assert lower_bound == (3 * LinExp("coeff_0_LL_min") +
                           -4 * LinExp("coeff_0_LL_max") +
                           30 * LinExp("coeff_1_HH_min") +
                           -40 * LinExp("coeff_1_HH_max") + -100 + 1)
    assert upper_bound == (-4 * LinExp("coeff_0_LL_min") +
                           3 * LinExp("coeff_0_LL_max") +
                           -40 * LinExp("coeff_1_HH_min") +
                           30 * LinExp("coeff_1_HH_max") + +100 + 1)
Exemplo n.º 27
0
def test_evaluate_analysis_filter_bounds():
    expr = LinExp(("pixel", 0, 0)) + 1000
    lower_bound_exp, upper_bound_exp = analysis_filter_bounds(expr)

    lower_bound, upper_bound = evaluate_analysis_filter_bounds(
        lower_bound_exp,
        upper_bound_exp,
        10,
    )

    assert isinstance(lower_bound, int)
    assert isinstance(upper_bound, int)

    assert lower_bound == 1000 - 512
    assert upper_bound == 1000 + 511
Exemplo n.º 28
0
def get_maximising_inputs(expression):
    """
    Find the symbol value assignment which maximises the provided expression.
    
    Parameters
    ==========
    expression : :py:class:`~vc2_bit_widths.linexp.LinExp`
        The expression whose value is to be maximised.
    
    Returns
    =======
    maximising_symbol_assignment : {sym: +1 or -1, ...}
        A dictionary giving the polarity of a maximising assignment to each
        non-:py:class:`~vc2_bit_widths.affine_arithmetic.Error` term in the
        expression.
    """
    return {
        sym: +1 if coeff > 0 else -1
        for sym, coeff in LinExp(expression)
        if sym is not None and not isinstance(sym, AAError)
    }
Exemplo n.º 29
0
 def get(self, keys):
     # What phase does the requested value lie on
     phase_offset = tuple(
         k % p
         for k, p in zip(keys, self._array.period)
     )
     # How many periods along each axis is the requested value
     period_number = tuple(
         k // p
         for k, p in zip(keys, self._array.period)
     )
     
     base_value = self._array[phase_offset]
     
     replacements = {}
     for sym in base_value.symbols():
         if sym is None or isinstance(sym, AAError):
             continue
         
         prefix = sym[0]
         component_keys = sym[1:]
         component_array = self._prefix_to_symbol_array[prefix]
         
         new_keys = tuple(
             (p*s) + o
             for p, s, o in zip(
                 period_number,
                 self._get_scale_factors(component_array),
                 component_keys,
             )
         )
         
         replacements[(prefix, ) + component_keys] = (prefix, ) + new_keys
     
     # Could use LinExp.subs but this direct implementation is substantially
     # faster.
     return LinExp({
         replacements.get(sym, sym): coeff
         for sym, coeff in base_value
     })
Exemplo n.º 30
0
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)