(e)
Simplify a Mul with TensorProducts.

    Current the main use of this is to simplify a ``Mul`` of ``TensorProduct``s
    to a ``TensorProduct`` of ``Muls``. It currently only works for relatively
    simple cases where the initial ``Mul`` only has scalars and raw
    ``TensorProduct``s, not ``Add``, ``Pow``, ``Commutator``s of
    ``TensorProduct``s.

    Parameters
    ==========

    e : Expr
        A ``Mul`` of ``TensorProduct``s to be simplified.

    Returns
    =======

    e : Expr
        A ``TensorProduct`` of ``Mul``s.

    Examples
    ========

    This is an example of the type of simplification that this function
    performs::

        >>> from sympy.physics.quantum.tensorproduct import \
                    tensor_product_simp_Mul, TensorProduct
        >>> from sympy import Symbol
        >>> A = Symbol('A',commutative=False)
        >>> B = Symbol('B',commutative=False)
        >>> C = Symbol('C',commutative=False)
        >>> D = Symbol('D',commutative=False)
        >>> e = TensorProduct(A,B)*TensorProduct(C,D)
        >>> e
        AxB*CxD
        >>> tensor_product_simp_Mul(e)
        (A*C)x(B*D)
Simplify a Mul with TensorProducts.
Simplify a Mul with TensorProducts .
def tensor_product_simp_Mul(e):
    """Simplify a Mul with TensorProducts.

    Current the main use of this is to simplify a ``Mul`` of ``TensorProduct``s
    to a ``TensorProduct`` of ``Muls``. It currently only works for relatively
    simple cases where the initial ``Mul`` only has scalars and raw
    ``TensorProduct``s, not ``Add``, ``Pow``, ``Commutator``s of
    ``TensorProduct``s.

    Parameters
    ==========

    e : Expr
        A ``Mul`` of ``TensorProduct``s to be simplified.

    Returns
    =======

    e : Expr
        A ``TensorProduct`` of ``Mul``s.

    Examples
    ========

    This is an example of the type of simplification that this function
    performs::

        >>> from sympy.physics.quantum.tensorproduct import \
                    tensor_product_simp_Mul, TensorProduct
        >>> from sympy import Symbol
        >>> A = Symbol('A',commutative=False)
        >>> B = Symbol('B',commutative=False)
        >>> C = Symbol('C',commutative=False)
        >>> D = Symbol('D',commutative=False)
        >>> e = TensorProduct(A,B)*TensorProduct(C,D)
        >>> e
        AxB*CxD
        >>> tensor_product_simp_Mul(e)
        (A*C)x(B*D)

    """
    # TODO: This won't work with Muls that have other composites of
    # TensorProducts, like an Add, Pow, Commutator, etc.
    # TODO: This only works for the equivalent of single Qbit gates.
    if not isinstance(e, Mul):
        return e
    c_part, nc_part = e.args_cnc()
    n_nc = len(nc_part)
    if n_nc == 0 or n_nc == 1:
        return e
    elif e.has(TensorProduct):
        current = nc_part[0]
        if not isinstance(current, TensorProduct):
            raise TypeError('TensorProduct expected, got: %r' % current)
        n_terms = len(current.args)
        new_args = list(current.args)
        for next in nc_part[1:]:
            # TODO: check the hilbert spaces of next and current here.
            if isinstance(next, TensorProduct):
                if n_terms != len(next.args):
                    raise QuantumError(
                        'TensorProducts of different lengths: %r and %r' %
                        (current, next)
                    )
                for i in range(len(new_args)):
                    new_args[i] = new_args[i] * next.args[i]
            else:
                # this won't quite work as we don't want next in the
                # TensorProduct
                for i in range(len(new_args)):
                    new_args[i] = new_args[i] * next
            current = next
        return Mul(*c_part) * TensorProduct(*new_args)
    else:
        return e
def tensor_product_simp_Mul ( e ) : # TODO: This won't work with Muls that have other composites of # TensorProducts, like an Add, Pow, Commutator, etc. # TODO: This only works for the equivalent of single Qbit gates. if not isinstance ( e , Mul ) : return e c_part , nc_part = e . args_cnc ( ) n_nc = len ( nc_part ) if n_nc == 0 or n_nc == 1 : return e elif e . has ( TensorProduct ) : current = nc_part [ 0 ] if not isinstance ( current , TensorProduct ) : raise TypeError ( 'TensorProduct expected, got: %r' % current ) n_terms = len ( current . args ) new_args = list ( current . args ) for next in nc_part [ 1 : ] : # TODO: check the hilbert spaces of next and current here. if isinstance ( next , TensorProduct ) : if n_terms != len ( next . args ) : raise QuantumError ( 'TensorProducts of different lengths: %r and %r' % ( current , next ) ) for i in range ( len ( new_args ) ) : new_args [ i ] = new_args [ i ] * next . args [ i ] else : # this won't quite work as we don't want next in the # TensorProduct for i in range ( len ( new_args ) ) : new_args [ i ] = new_args [ i ] * next current = next return Mul ( * c_part ) * TensorProduct ( * new_args ) else : return e