(f, g)
return (f.components + g.components, free, dum)
The algorithms performing the multiplication of two ``TIDS`` instances.

        In short, it forms a new ``TIDS`` object, joining components and indices,
        checking that abstract indices are compatible, and possibly contracting
        them.

        Examples
        ========

        >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TIDS, tensorhead
        >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
        >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz)
        >>> T = tensorhead('T', [Lorentz]*4, [[1]*4])
        >>> A = tensorhead('A', [Lorentz], [[1]])
        >>> tids_1 = TIDS.from_components_and_indices([T], [m0, m1, -m1, m3])
        >>> tids_2 = TIDS.from_components_and_indices([A], [m2])
        >>> tids_1 * tids_2
        TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz), A(Lorentz)],\
            [(m0, 0, 0), (m3, 3, 0), (m2, 0, 1)], [(1, 2, 0, 0)])

        In this case no contraction has been performed.

        >>> tids_3 = TIDS.from_components_and_indices([A], [-m3])
        >>> tids_1 * tids_3
        TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz), A(Lorentz)],\
            [(m0, 0, 0)], [(1, 2, 0, 0), (3, 0, 0, 1)])

        Free indices ``m3`` and ``-m3`` are identified as a contracted couple, and are
        therefore transformed into dummy indices.

        A wrong index construction (for example, trying to contract two
        contravariant indices or using indices multiple times) would result in
        an exception:

        >>> tids_4 = TIDS.from_components_and_indices([A], [m3])
        >>> # This raises an exception:
        >>> # tids_1 * tids_4
The algorithms performing the multiplication of two ``TIDS`` instances.
The algorithms performing the multiplication of two TIDS instances .
def mul(f, g):
        """
        The algorithms performing the multiplication of two ``TIDS`` instances.

        In short, it forms a new ``TIDS`` object, joining components and indices,
        checking that abstract indices are compatible, and possibly contracting
        them.

        Examples
        ========

        >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TIDS, tensorhead
        >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
        >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz)
        >>> T = tensorhead('T', [Lorentz]*4, [[1]*4])
        >>> A = tensorhead('A', [Lorentz], [[1]])
        >>> tids_1 = TIDS.from_components_and_indices([T], [m0, m1, -m1, m3])
        >>> tids_2 = TIDS.from_components_and_indices([A], [m2])
        >>> tids_1 * tids_2
        TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz), A(Lorentz)],\
            [(m0, 0, 0), (m3, 3, 0), (m2, 0, 1)], [(1, 2, 0, 0)])

        In this case no contraction has been performed.

        >>> tids_3 = TIDS.from_components_and_indices([A], [-m3])
        >>> tids_1 * tids_3
        TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz), A(Lorentz)],\
            [(m0, 0, 0)], [(1, 2, 0, 0), (3, 0, 0, 1)])

        Free indices ``m3`` and ``-m3`` are identified as a contracted couple, and are
        therefore transformed into dummy indices.

        A wrong index construction (for example, trying to contract two
        contravariant indices or using indices multiple times) would result in
        an exception:

        >>> tids_4 = TIDS.from_components_and_indices([A], [m3])
        >>> # This raises an exception:
        >>> # tids_1 * tids_4
        """
        index_up = lambda u: u if u.is_up else -u

        # find out which free indices of f and g are contracted
        free_dict1 = dict([(i if i.is_up else -i, (pos, cpos, i)) for i, pos, cpos in f.free])
        free_dict2 = dict([(i if i.is_up else -i, (pos, cpos, i)) for i, pos, cpos in g.free])

        free_names = set(free_dict1.keys()) & set(free_dict2.keys())
        # find the new `free` and `dum`
        nc1 = len(f.components)
        dum2 = [(i1, i2, c1 + nc1, c2 + nc1) for i1, i2, c1, c2 in g.dum]
        free1 = [(ind, i, c) for ind, i, c in f.free if index_up(ind) not in free_names]
        free2 = [(ind, i, c + nc1) for ind, i, c in g.free if index_up(ind) not in free_names]
        free = free1 + free2
        dum = f.dum + dum2
        for name in free_names:
            ipos1, cpos1, ind1 = free_dict1[name]
            ipos2, cpos2, ind2 = free_dict2[name]
            cpos2 += nc1
            if ind1._is_up == ind2._is_up:
                raise ValueError('wrong index construction {0}'.format(ind1))
            if ind1._is_up:
                new_dummy = (ipos1, ipos2, cpos1, cpos2)
            else:
                new_dummy = (ipos2, ipos1, cpos2, cpos1)
            dum.append(new_dummy)
        return (f.components + g.components, free, dum)
def mul ( f , g ) : index_up = lambda u : u if u . is_up else - u # find out which free indices of f and g are contracted free_dict1 = dict ( [ ( i if i . is_up else - i , ( pos , cpos , i ) ) for i , pos , cpos in f . free ] ) free_dict2 = dict ( [ ( i if i . is_up else - i , ( pos , cpos , i ) ) for i , pos , cpos in g . free ] ) free_names = set ( free_dict1 . keys ( ) ) & set ( free_dict2 . keys ( ) ) # find the new `free` and `dum` nc1 = len ( f . components ) dum2 = [ ( i1 , i2 , c1 + nc1 , c2 + nc1 ) for i1 , i2 , c1 , c2 in g . dum ] free1 = [ ( ind , i , c ) for ind , i , c in f . free if index_up ( ind ) not in free_names ] free2 = [ ( ind , i , c + nc1 ) for ind , i , c in g . free if index_up ( ind ) not in free_names ] free = free1 + free2 dum = f . dum + dum2 for name in free_names : ipos1 , cpos1 , ind1 = free_dict1 [ name ] ipos2 , cpos2 , ind2 = free_dict2 [ name ] cpos2 += nc1 if ind1 . _is_up == ind2 . _is_up : raise ValueError ( 'wrong index construction {0}' . format ( ind1 ) ) if ind1 . _is_up : new_dummy = ( ipos1 , ipos2 , cpos1 , cpos2 ) else : new_dummy = ( ipos2 , ipos1 , cpos2 , cpos1 ) dum . append ( new_dummy ) return ( f . components + g . components , free , dum )