Example #1
0
def test_ccode_loops_matrix_vector():
    n, m = symbols('n m', integer=True)
    A = IndexedBase('A')
    x = IndexedBase('x')
    y = IndexedBase('y')
    i = Idx('i', m)
    j = Idx('j', n)

    s = ('for (int i=0; i<m; i++){\n'
         '   y[i] = 0;\n'
         '}\n'
         'for (int i=0; i<m; i++){\n'
         '   for (int j=0; j<n; j++){\n'
         '      y[i] = x[j]*A[%s] + y[i];\n' % (i * n + j) + '   }\n'
         '}')
    c = ccode(A[i, j] * x[j], assign_to=y[i])
    assert c == s

    pytest.raises(ValueError, lambda: ccode(A[i, j] * x[j], assign_to=x[j]))

    s2 = ('for (int i=0; i<m; i++){\n'
          '   y[i] = 0;\n'
          '}\n'
          'for (int i=0; i<m; i++){\n'
          '   for (int i=0; i<m; i++){\n'
          '      y[i] = y[i] + A[m*i + i];\n'
          '   }\n}')
    c = ccode(A[i, i], assign_to=y[i])
    assert c == s2
Example #2
0
def test_m_tensor_loops_multiple_contractions():
    # see comments in previous test about vectorizing
    n, m, o, p = symbols('n m o p', integer=True)
    A = IndexedBase('A')
    B = IndexedBase('B')
    y = IndexedBase('y')
    i = Idx('i', m)
    j = Idx('j', n)
    k = Idx('k', o)
    l = Idx('l', p)
    result, = codegen(('tensorthing', Eq(y[i], B[j, k, l] * A[i, j, k, l])),
                      'Octave',
                      header=False,
                      empty=False)
    source = result[1]
    expected = ('function y = tensorthing(A, B, m, n, o, p)\n'
                '  for i = 1:m\n'
                '    y(i) = 0;\n'
                '  end\n'
                '  for i = 1:m\n'
                '    for j = 1:n\n'
                '      for k = 1:o\n'
                '        for l = 1:p\n'
                '          y(i) = y(i) + B(j, k, l).*A(i, j, k, l);\n'
                '        end\n'
                '      end\n'
                '    end\n'
                '  end\n'
                'end\n')
    assert source == expected
Example #3
0
def test_ccode_loops_multiple_contractions():
    n, m, o, p = symbols('n m o p', integer=True)
    a = IndexedBase('a')
    b = IndexedBase('b')
    y = IndexedBase('y')
    i = Idx('i', m)
    j = Idx('j', n)
    k = Idx('k', o)
    l = Idx('l', p)

    s = ('for (int i=0; i<m; i++){\n'
         '   y[i] = 0;\n'
         '}\n'
         'for (int i=0; i<m; i++){\n'
         '   for (int j=0; j<n; j++){\n'
         '      for (int k=0; k<o; k++){\n'
         '         for (int l=0; l<p; l++){\n'
         '            y[i] = y[i] + b[%s]*a[%s];\n' %
         (j * o * p + k * p + l, i * n * o * p + j * o * p + k * p + l) +
         '         }\n'
         '      }\n'
         '   }\n'
         '}')
    c = ccode(b[j, k, l] * a[i, j, k, l], assign_to=y[i])
    assert c == s
Example #4
0
def test_ccode_loops_addfactor():
    n, m, o, p = symbols('n m o p', integer=True)
    a = IndexedBase('a')
    b = IndexedBase('b')
    c = IndexedBase('c')
    y = IndexedBase('y')
    i = Idx('i', m)
    j = Idx('j', n)
    k = Idx('k', o)
    l = Idx('l', p)

    s = ('for (int i=0; i<m; i++){\n'
         '   y[i] = 0;\n'
         '}\n'
         'for (int i=0; i<m; i++){\n'
         '   for (int j=0; j<n; j++){\n'
         '      for (int k=0; k<o; k++){\n'
         '         for (int l=0; l<p; l++){\n'
         '            y[i] = (a[%s] + b[%s])*c[%s] + y[i];\n' %
         (i * n * o * p + j * o * p + k * p + l, i * n * o * p + j * o * p +
          k * p + l, j * o * p + k * p + l) + '         }\n'
         '      }\n'
         '   }\n'
         '}')
    c = ccode((a[i, j, k, l] + b[i, j, k, l]) * c[j, k, l], assign_to=y[i])
    assert c == s
def test_contraction_structure_Mul_and_Pow():
    x = IndexedBase('x')
    y = IndexedBase('y')
    i, j, k = Idx('i'), Idx('j'), Idx('k')

    i_ji = x[i]**(y[j] * x[i])
    assert get_contraction_structure(i_ji) == {None: {i_ji}}
    ij_i = (x[i] * y[j])**(y[i])
    assert get_contraction_structure(ij_i) == {None: {ij_i}}
    j_ij_i = x[j] * (x[i] * y[j])**(y[i])
    assert get_contraction_structure(j_ij_i) == {(j, ): {j_ij_i}}
    j_i_ji = x[j] * x[i]**(y[j] * x[i])
    assert get_contraction_structure(j_i_ji) == {(j, ): {j_i_ji}}
    ij_exp_kki = x[i] * y[j] * exp(y[i] * y[k, k])
    result = get_contraction_structure(ij_exp_kki)
    expected = {
        (i, ): {ij_exp_kki},
        ij_exp_kki: [{
            None: {exp(y[i] * y[k, k])},
            exp(y[i] * y[k, k]): [{
                None: {y[i] * y[k, k]},
                y[i] * y[k, k]: [{
                    (k, ): {y[k, k]}
                }]
            }]
        }]
    }
    assert result == expected
Example #6
0
def test_loops():
    n, m = symbols('n,m', integer=True)
    A = IndexedBase('A')
    x = IndexedBase('x')
    y = IndexedBase('y')
    i = Idx('i', m)
    j = Idx('j', n)

    expected = ('do i = 1, m\n'
                '   y(i) = 0\n'
                'end do\n'
                'do i = 1, m\n'
                '   do j = 1, n\n'
                '      y(i) = %(rhs)s\n'
                '   end do\n'
                'end do')

    code = fcode(A[i, j] * x[j], assign_to=y[i], source_format='free')
    assert (code == expected % {
        'rhs': 'y(i) + A(i, j)*x(j)'
    } or code == expected % {
        'rhs': 'y(i) + x(j)*A(i, j)'
    } or code == expected % {
        'rhs': 'x(j)*A(i, j) + y(i)'
    } or code == expected % {
        'rhs': 'A(i, j)*x(j) + y(i)'
    })
Example #7
0
def test_ccode_loops_multiple_terms():
    n, m, o, p = symbols('n m o p', integer=True)
    a = IndexedBase('a')
    b = IndexedBase('b')
    c = IndexedBase('c')
    y = IndexedBase('y')
    i = Idx('i', m)
    j = Idx('j', n)
    k = Idx('k', o)

    s0 = ('for (int i=0; i<m; i++){\n' '   y[i] = 0;\n' '}\n')
    s1 = ('for (int i=0; i<m; i++){\n'
          '   for (int j=0; j<n; j++){\n'
          '      for (int k=0; k<o; k++){\n'
          '         y[i] = b[j]*b[k]*c[%s] + y[i];\n' %
          (i * n * o + j * o + k) + '      }\n'
          '   }\n'
          '}\n')
    s2 = ('for (int i=0; i<m; i++){\n'
          '   for (int k=0; k<o; k++){\n'
          '      y[i] = b[k]*a[%s] + y[i];\n' % (i * o + k) + '   }\n'
          '}\n')
    s3 = ('for (int i=0; i<m; i++){\n'
          '   for (int j=0; j<n; j++){\n'
          '      y[i] = b[j]*a[%s] + y[i];\n' % (i * n + j) + '   }\n'
          '}\n')
    c = ccode(b[j] * a[i, j] + b[k] * a[i, k] + b[j] * b[k] * c[i, j, k],
              assign_to=y[i])
    assert (c == s0 + s1 + s2 + s3[:-1] or c == s0 + s1 + s3 + s2[:-1]
            or c == s0 + s2 + s1 + s3[:-1] or c == s0 + s2 + s3 + s1[:-1]
            or c == s0 + s3 + s1 + s2[:-1] or c == s0 + s3 + s2 + s1[:-1])
Example #8
0
def test_Idx_subs():
    i, a, b = symbols('i a b', integer=True)
    assert Idx(i, a).subs(a, b) == Idx(i, b)
    assert Idx(i, a).subs(i, b) == Idx(b, a)

    assert Idx(i).subs(i, 2) == Idx(2)
    assert Idx(i, a).subs(a, 2) == Idx(i, 2)
    assert Idx(i, (a, b)).subs(i, 2) == Idx(2, (a, b))
def test_get_contraction_structure_complex():
    x = IndexedBase('x')
    y = IndexedBase('y')
    A = IndexedBase('A')
    i, j, k = Idx('i'), Idx('j'), Idx('k')
    expr1 = y[i] + A[i, j] * x[j]
    d1 = {None: {y[i]}, (j, ): {A[i, j] * x[j]}}
    assert get_contraction_structure(expr1) == d1
    expr2 = expr1 * A[k, i] + x[k]
    d2 = {None: {x[k]}, (i, ): {expr1 * A[k, i]}, expr1 * A[k, i]: [d1]}
    assert get_contraction_structure(expr2) == d2
Example #10
0
def test_Idx_func_args():
    i, a, b = symbols('i a b', integer=True)
    ii = Idx(i)
    assert ii.func(*ii.args) == ii
    ii = Idx(i, a)
    assert ii.func(*ii.args) == ii
    ii = Idx(i, (a, b))
    assert ii.func(*ii.args) == ii
Example #11
0
def test_m_loops():
    # Note: an Octave programmer would probably vectorize this across one or
    # more dimensions.  Also, size(A) would be used rather than passing in m
    # and n.  Perhaps users would expect us to vectorize automatically here?
    # Or is it possible to represent such things using IndexedBase?
    n, m = symbols('n m', integer=True)
    A = IndexedBase('A')
    x = IndexedBase('x')
    y = IndexedBase('y')
    i = Idx('i', m)
    j = Idx('j', n)
    result, = codegen(('mat_vec_mult', Eq(y[i], A[i, j] * x[j])),
                      'Octave',
                      header=False,
                      empty=False)
    source = result[1]
    expected = ('function y = mat_vec_mult(A, m, n, x)\n'
                '  for i = 1:m\n'
                '    y(i) = 0;\n'
                '  end\n'
                '  for i = 1:m\n'
                '    for j = 1:n\n'
                '      y(i) = %(rhs)s + y(i);\n'
                '    end\n'
                '  end\n'
                'end\n')
    assert (source == expected % {
        'rhs': 'A(%s, %s).*x(j)' % (i, j)
    } or source == expected % {
        'rhs': 'x(j).*A(%s, %s)' % (i, j)
    })

    result, = codegen(('mat_vec_mult', Eq(y[i], A[i, j] * x[j])),
                      'Octave',
                      header=False,
                      empty=False,
                      argument_sequence=[x, A, m, n])
    source = result[1]
    expected = ('function y = mat_vec_mult(x, A, m, n)\n'
                '  for i = 1:m\n'
                '    y(i) = 0;\n'
                '  end\n'
                '  for i = 1:m\n'
                '    for j = 1:n\n'
                '      y(i) = %(rhs)s + y(i);\n'
                '    end\n'
                '  end\n'
                'end\n')
    assert (source == expected % {
        'rhs': 'A(%s, %s).*x(j)' % (i, j)
    } or source == expected % {
        'rhs': 'x(j).*A(%s, %s)' % (i, j)
    })
def test_contraction_structure_simple_Pow():
    x = IndexedBase('x')
    y = IndexedBase('y')
    i, j = Idx('i'), Idx('j')
    ii_jj = x[i, i]**y[j, j]
    assert get_contraction_structure(ii_jj) == {
        None: {ii_jj},
        ii_jj: [{
            (i, ): {x[i, i]}
        }, {
            (j, ): {y[j, j]}
        }]
    }
Example #13
0
def test_ccode_Indexed():
    n, m, o = symbols('n m o', integer=True)
    i, j, k = Idx('i', n), Idx('j', m), Idx('k', o)
    p = CCodePrinter()
    p._not_c = set()

    x = IndexedBase('x')[j]
    assert p._print_Indexed(x) == 'x[j]'
    A = IndexedBase('A')[i, j]
    assert p._print_Indexed(A) == 'A[%s]' % (m * i + j)
    B = IndexedBase('B')[i, j, k]
    assert p._print_Indexed(B) == 'B[%s]' % (i * o * m + j * o + k)

    assert p._not_c == set()
def test_get_indices_Pow():
    x = IndexedBase('x')
    y = IndexedBase('y')
    A = IndexedBase('A')
    i, j, k = Idx('i'), Idx('j'), Idx('k')
    assert get_indices(Pow(x[i], y[j])) == ({i, j}, {})
    assert get_indices(Pow(x[i, k], y[j, k])) == ({i, j, k}, {})
    assert get_indices(Pow(A[i, k], y[k] + A[k, j] * x[j])) == ({i, k}, {})
    assert get_indices(Pow(2, x[i])) == get_indices(exp(x[i]))

    # test of a design decision, this may change:
    assert get_indices(Pow(x[i], 2)) == ({
        i,
    }, {})
Example #15
0
def test_Assignment():
    x, y = symbols('x, y')
    A = MatrixSymbol('A', 3, 1)
    mat = Matrix([1, 2, 3])
    B = IndexedBase('B')
    n = symbols('n', integer=True)
    i = Idx('i', n)
    # Here we just do things to show they don't error
    Assignment(x, y)
    Assignment(x, 0)
    Assignment(A, mat)
    Assignment(A[1, 0], 0)
    Assignment(A[1, 0], x)
    Assignment(B[i], x)
    Assignment(B[i], 0)
    # Here we test things to show that they error
    # Matrix to scalar
    pytest.raises(ValueError, lambda: Assignment(B[i], A))
    pytest.raises(ValueError, lambda: Assignment(B[i], mat))
    pytest.raises(ValueError, lambda: Assignment(x, mat))
    pytest.raises(ValueError, lambda: Assignment(x, A))
    pytest.raises(ValueError, lambda: Assignment(A[1, 0], mat))
    # Scalar to matrix
    pytest.raises(ValueError, lambda: Assignment(A, x))
    pytest.raises(ValueError, lambda: Assignment(A, 0))
    # Non-atomic lhs
    pytest.raises(TypeError, lambda: Assignment(mat, A))
    pytest.raises(TypeError, lambda: Assignment(0, x))
    pytest.raises(TypeError, lambda: Assignment(x * x, 1))
    pytest.raises(TypeError, lambda: Assignment(A + A, mat))
    pytest.raises(TypeError, lambda: Assignment(B, 0))
Example #16
0
def test_Indexed_coeff():
    N = Symbol('N', integer=True)
    len_y = N
    i = Idx('i', len_y - 1)
    y = IndexedBase('y', shape=(len_y, ))
    a = (1 / y[i + 1] * y[i]).coeff(y[i])
    b = (y[i] / y[i + 1]).coeff(y[i])
    assert a == b
def test_contraction_structure_Add_in_Pow():
    x = IndexedBase('x')
    y = IndexedBase('y')
    i, j = Idx('i'), Idx('j')
    s_ii_jj_s = (1 + x[i, i])**(1 + y[j, j])
    expected = {
        None: {s_ii_jj_s},
        s_ii_jj_s: [{
            None: {1},
            (i, ): {x[i, i]}
        }, {
            None: {1},
            (j, ): {y[j, j]}
        }]
    }
    result = get_contraction_structure(s_ii_jj_s)
    assert result == expected
Example #18
0
def test_ccode_Indexed_without_looking_for_contraction():
    len_y = 5
    y = IndexedBase('y', shape=(len_y, ))
    x = IndexedBase('x', shape=(len_y, ))
    Dy = IndexedBase('Dy', shape=(len_y - 1, ))
    i = Idx('i', len_y - 1)
    e = Eq(Dy[i], (y[i + 1] - y[i]) / (x[i + 1] - x[i]))
    code0 = ccode(e.rhs, assign_to=e.lhs, contract=False)
    assert code0 == 'Dy[i] = (y[%s] - y[i])/(x[%s] - x[i]);' % (i + 1, i + 1)
Example #19
0
def test_ccode_loops_add():
    n, m = symbols('n m', integer=True)
    A = IndexedBase('A')
    x = IndexedBase('x')
    y = IndexedBase('y')
    z = IndexedBase('z')
    i = Idx('i', m)
    j = Idx('j', n)

    s = ('for (int i=0; i<m; i++){\n'
         '   y[i] = x[i] + z[i];\n'
         '}\n'
         'for (int i=0; i<m; i++){\n'
         '   for (int j=0; j<n; j++){\n'
         '      y[i] = x[j]*A[%s] + y[i];\n' % (i * n + j) + '   }\n'
         '}')
    c = ccode(A[i, j] * x[j] + x[i] + z[i], assign_to=y[i])
    assert c == s
Example #20
0
def test_fcode_Indexed_without_looking_for_contraction():
    len_y = 5
    y = IndexedBase('y', shape=(len_y, ))
    x = IndexedBase('x', shape=(len_y, ))
    Dy = IndexedBase('Dy', shape=(len_y - 1, ))
    i = Idx('i', len_y - 1)
    e = Eq(Dy[i], (y[i + 1] - y[i]) / (x[i + 1] - x[i]))
    code0 = fcode(e.rhs, assign_to=e.lhs, contract=False)
    assert code0.endswith('Dy(i) = (y(i + 1) - y(i))/(x(i + 1) - x(i))')
Example #21
0
def test_Indexed_shape_precedence():
    i, j = symbols('i j', integer=True)
    o, p = symbols('o p', integer=True)
    n, m = symbols('n m', integer=True)
    a = IndexedBase('a', shape=(o, p))
    assert a.shape == Tuple(o, p)
    assert Indexed(
        a, Idx(i, m), Idx(j, n)).ranges == [Tuple(0, m - 1), Tuple(0, n - 1)]
    assert Indexed(a, Idx(i, m), Idx(j, n)).shape == Tuple(o, p)
    assert Indexed(
        a, Idx(i, m), Idx(j)).ranges == [Tuple(0, m - 1), Tuple(None, None)]
    assert Indexed(a, Idx(i, m), Idx(j)).shape == Tuple(o, p)
Example #22
0
def test_cse_Indexed():
    len_y = 5
    y = IndexedBase('y', shape=(len_y, ))
    x = IndexedBase('x', shape=(len_y, ))
    i = Idx('i', len_y - 1)

    expr1 = (y[i + 1] - y[i]) / (x[i + 1] - x[i])
    expr2 = 1 / (x[i + 1] - x[i])
    replacements, reduced_exprs = cse([expr1, expr2])
    assert len(replacements) > 0
def test_get_contraction_structure_basic():
    x = IndexedBase('x')
    y = IndexedBase('y')
    i, j = Idx('i'), Idx('j')
    f = Function('f')
    assert get_contraction_structure(x[i] * y[j]) == {None: {x[i] * y[j]}}
    assert get_contraction_structure(x[i] + y[j]) == {None: {x[i], y[j]}}
    assert get_contraction_structure(x[i] * y[i]) == {(i, ): {x[i] * y[i]}}
    assert get_contraction_structure(1 + x[i] * y[i]) == {
        None: {1},
        (i, ): {x[i] * y[i]}
    }
    assert get_contraction_structure(x[i]**y[i]) == {None: {x[i]**y[i]}}
    assert (get_contraction_structure(f(x[i, i])) == {
        None: {f(x[i, i])},
        f(x[i, i]): [{
            (i, ): {x[i, i]}
        }]
    })
def test_contraction_structure_Pow_in_Pow():
    x = IndexedBase('x')
    y = IndexedBase('y')
    z = IndexedBase('z')
    i, j, k = Idx('i'), Idx('j'), Idx('k')
    ii_jj_kk = x[i, i]**y[j, j]**z[k, k]
    expected = {
        None: {ii_jj_kk},
        ii_jj_kk: [{
            (i, ): {x[i, i]}
        }, {
            None: {y[j, j]**z[k, k]},
            y[j, j]**z[k, k]: [{
                (j, ): {y[j, j]}
            }, {
                (k, ): {z[k, k]}
            }]
        }]
    }
    assert get_contraction_structure(ii_jj_kk) == expected
def test_get_indices_add():
    x = IndexedBase('x')
    y = IndexedBase('y')
    A = IndexedBase('A')
    i, j, k = Idx('i'), Idx('j'), Idx('k')
    assert get_indices(x[i] + 2 * y[i]) == ({
        i,
    }, {})
    assert get_indices(y[i] + 2 * A[i, j] * x[j]) == ({
        i,
    }, {})
    assert get_indices(y[i] + 2 * (x[i] + A[i, j] * x[j])) == ({
        i,
    }, {})
    assert get_indices(y[i] + x[i] * (A[j, j] + 1)) == ({
        i,
    }, {})
    assert get_indices(y[i] + x[i] * x[j] * (y[j] + A[j, k] * x[k])) == ({
        i,
    }, {})
Example #26
0
def test_ccode_inline_function():
    g = implemented_function('g', Lambda(x, 2 * x))
    assert ccode(g(x)) == '2*x'
    g = implemented_function('g', Lambda(x, 2 * x / Catalan))
    assert ccode(
        g(x)) == 'double const Catalan = %s;\n2*x/Catalan' % Catalan.evalf()
    A = IndexedBase('A')
    i = Idx('i', symbols('n', integer=True))
    g = implemented_function('g', Lambda(x, x * (1 + x) * (2 + x)))
    assert ccode(g(A[i]),
                 assign_to=A[i]) == ('for (int i=0; i<n; i++){\n'
                                     '   A[i] = (A[i] + 1)*(A[i] + 2)*A[i];\n'
                                     '}')
Example #27
0
def test_dummy_loops():
    i, m = symbols('i m', integer=True, cls=Dummy)
    x = IndexedBase('x')
    y = IndexedBase('y')
    i = Idx(i, m)

    expected = (
        'do i_%(icount)i = 1, m_%(mcount)i\n'
        '   y(i_%(icount)i) = x(i_%(icount)i)\n'
        'end do'
    ) % {'icount': i.label.dummy_index, 'mcount': m.dummy_index}
    code = fcode(x[i], assign_to=y[i], source_format='free')
    assert code == expected
Example #28
0
def test_dummy_loops():
    i, m = symbols('i m', integer=True, cls=Dummy)
    x = IndexedBase('x')
    y = IndexedBase('y')
    i = Idx(i, m)

    expected = (
        'for (int i_%(icount)i=0; i_%(icount)i<m_%(mcount)i; i_%(icount)i++){\n'
        '   y[i_%(icount)i] = x[i_%(icount)i];\n'
        '}'
    ) % {'icount': i.label.dummy_index, 'mcount': m.dummy_index}
    code = ccode(x[i], assign_to=y[i])
    assert code == expected
Example #29
0
def test_Idx_func_args():
    i, a, b = symbols('i a b', integer=True)
    ii = Idx(i)
    assert ii.func(*ii.args) == ii
    ii = Idx(i, a)
    assert ii.func(*ii.args) == ii
    ii = Idx(i, (a, b))
    assert ii.func(*ii.args) == ii
Example #30
0
def test_inline_function():
    g = implemented_function('g', Lambda(x, 2 * x))
    assert fcode(g(x)) == '      2*x'
    g = implemented_function('g', Lambda(x, 2 * pi / x))
    assert fcode(g(x)) == ('      parameter (pi = 3.14159265358979d0)\n'
                           '      2*pi/x')
    A = IndexedBase('A')
    i = Idx('i', symbols('n', integer=True))
    g = implemented_function('g', Lambda(x, x * (1 + x) * (2 + x)))
    assert fcode(
        g(A[i]),
        assign_to=A[i]) == ('      do i = 1, n\n'
                            '         A(i) = (A(i) + 1)*(A(i) + 2)*A(i)\n'
                            '      end do')
def test_ufunc_support():
    f = Function('f')
    g = Function('g')
    x = IndexedBase('x')
    y = IndexedBase('y')
    i, j = Idx('i'), Idx('j')

    assert get_indices(f(x[i])) == ({i}, {})
    assert get_indices(f(x[i], y[j])) == ({i, j}, {})
    assert get_indices(f(y[i]) * g(x[i])) == (set(), {})
    assert get_indices(f(a, x[i])) == ({i}, {})
    assert get_indices(f(a, y[i], x[j]) * g(x[i])) == ({j}, {})
    assert get_indices(g(f(x[i]))) == ({i}, {})

    assert get_contraction_structure(f(x[i])) == {None: {f(x[i])}}
    assert get_contraction_structure(f(y[i]) * g(x[i])) == {
        (i, ): {f(y[i]) * g(x[i])}
    }
    assert get_contraction_structure(f(y[i]) * g(f(x[i]))) == {
        (i, ): {f(y[i]) * g(f(x[i]))}
    }
    assert get_contraction_structure(f(x[j], y[i]) * g(x[i])) == {
        (i, ): {f(x[j], y[i]) * g(x[i])}
    }