Ejemplo n.º 1
0
def q_log(q, u):
    """
    Determines, if possible, an integer n such that q^n = u.

    Requires that both q and u belong to either QQ or some rational function field over QQ.

    q must not be zero or a root of unity.

    A ValueError is thrown if no n exists. 
    """
    if q in QQ and u in QQ:
        qq, uu = q, u
    else:
        q, u = canonical_coercion(q, u)
        ev = dict((y, hash(y)) for y in u.parent().gens_dict_recursive())
        qq, uu = q(**ev), u(**ev)

    n = ComplexField(53)(uu.n().log() / qq.n().log()).real_part().round()
    if q**n == u:
        return n
    else:
        raise ValueError
Ejemplo n.º 2
0
    def __init__(self, x, universe=None, check=True, immutable=False,
                 cr=False, cr_str=None, use_sage_types=False):
        """
        Create a sequence.

        EXAMPLES::

            sage: Sequence([1..5])
            [1, 2, 3, 4, 5]
            sage: a = Sequence([1..3], universe=QQ, check=False, immutable=True, cr=True, cr_str=False, use_sage_types=True)
            sage: a
            [
            1,
            2,
            3
            ]
            sage: a = Sequence([1..5], universe=QQ, check=False, immutable=True, cr_str=True, use_sage_types=True)
            sage: a
            [1, 2, 3, 4, 5]
            sage: a._Sequence__cr_str
            True
            sage: a.__str__()
            '[\n1,\n2,\n3,\n4,\n5\n]'
        """
        if not isinstance(x, (list, tuple)):
            x = list(x)
            #raise TypeError, "x must be a list or tuple"
        self.__hash = None

        
        self.__cr = cr
        if cr_str is None:
            self.__cr_str = cr
        else:
            self.__cr_str = cr_str

        if isinstance(x, Sequence):
            if universe is None or universe == x.__universe:
                list.__init__(self, x)
                self.__universe = x.__universe
                self._is_immutable = immutable
                return
        if universe is None:
            if len(x) == 0:
                import sage.categories.all
                universe = sage.categories.all.Objects()
            else:
                import sage.structure.element as coerce
                y = x
                x = list(x)   # make a copy, or we'd change the type of the elements of x, which would be bad.
                if use_sage_types:
                    # convert any Python builtin numerical types to Sage objects
                    from sage.rings.integer_ring import ZZ
                    from sage.rings.real_double import RDF
                    from sage.rings.complex_double import CDF
                    for i in range(len(x)):
                        if isinstance(x[i], int) or isinstance(x[i], long):
                            x[i] = ZZ(x[i])
                        elif isinstance(x[i], float):
                            x[i] = RDF(x[i])
                        elif isinstance(x[i], complex):
                            x[i] = CDF(x[i])
                # start the pairwise coercion
                for i in range(len(x)-1):
                    try:
                        x[i], x[i+1] = coerce.canonical_coercion(x[i],x[i+1])
                    except TypeError:
                        import sage.categories.all
                        universe = sage.categories.all.Objects()
                        x = list(y)
                        check = False  # no point
                        break
                if universe is None:   # no type errors raised.
                    universe = coerce.parent(x[len(x)-1])
                #universe = sage.structure.coerce.parent(x[0])
        self.__universe = universe
        if check:
            x = [universe(t) for t in x]
        list.__init__(self, x)
        self._is_immutable = immutable
Ejemplo n.º 3
0
def Sequence(x, universe=None, check=True, immutable=False, cr=False, cr_str=None, use_sage_types=False):
    """
    A mutable list of elements with a common guaranteed universe,
    which can be set immutable.

    A universe is either an object that supports coercion (e.g., a
    parent), or a category.

    INPUT:

    - ``x`` - a list or tuple instance

    - ``universe`` - (default: None) the universe of elements; if None
      determined using canonical coercions and the entire list of
      elements.  If list is empty, is category Objects() of all
      objects.

    - ``check`` -- (default: True) whether to coerce the elements of x
      into the universe

    - ``immutable`` - (default: True) whether or not this sequence is
      immutable

    - ``cr`` - (default: False) if True, then print a carriage return
      after each comma when printing this sequence.

    - ``cr_str`` - (default: False) if True, then print a carriage return
      after each comma when calling ``str()`` on this sequence.

    - ``use_sage_types`` -- (default: False) if True, coerce the
       built-in Python numerical types int, long, float, complex to the
       corresponding Sage types (this makes functions like vector()
       more flexible)

    OUTPUT:

    - a sequence

    EXAMPLES::

        sage: v = Sequence(range(10))
        sage: v.universe()
        <type 'int'>
        sage: v
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    We can request that the built-in Python numerical types be coerced
    to Sage objects::

        sage: v = Sequence(range(10), use_sage_types=True)
        sage: v.universe()
        Integer Ring
        sage: v
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    You can also use seq for "Sequence", which is identical to using
    Sequence::

        sage: v = seq([1,2,1/1]); v
        [1, 2, 1]
        sage: v.universe()
        Rational Field
        sage: v.parent()
        Category of sequences in Rational Field
        sage: v.parent()([3,4/3])
        [3, 4/3]


    Note that assignment coerces if possible,::

        sage: v = Sequence(range(10), ZZ)
        sage: a = QQ(5)
        sage: v[3] = a
        sage: parent(v[3])
        Integer Ring
        sage: parent(a)
        Rational Field
        sage: v[3] = 2/3
        Traceback (most recent call last):
        ...
        TypeError: no conversion of this rational to integer

    Sequences can be used absolutely anywhere lists or tuples can be used::

        sage: isinstance(v, list)
        True

    Sequence can be immutable, so entries can't be changed::

        sage: v = Sequence([1,2,3], immutable=True)
        sage: v.is_immutable()
        True
        sage: v[0] = 5
        Traceback (most recent call last):
        ...
        ValueError: object is immutable; please change a copy instead.

    Only immutable sequences are hashable (unlike Python lists),
    though the hashing is potentially slow, since it first involves
    conversion of the sequence to a tuple, and returning the hash of
    that.::

        sage: v = Sequence(range(10), ZZ, immutable=True)
        sage: hash(v)
        1591723448             # 32-bit
        -4181190870548101704   # 64-bit


    If you really know what you are doing, you can circumvent the type
    checking (for an efficiency gain)::

        sage: list.__setitem__(v, int(1), 2/3)        # bad circumvention
        sage: v
        [0, 2/3, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: list.__setitem__(v, int(1), int(2))     # not so bad circumvention

    You can make a sequence with a new universe from an old sequence.::

        sage: w = Sequence(v, QQ)
        sage: w
        [0, 2, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: w.universe()
        Rational Field
        sage: w[1] = 2/3
        sage: w
        [0, 2/3, 2, 3, 4, 5, 6, 7, 8, 9]

    Sequences themselves live in a category, the category of all sequences
    in the given universe.::

        sage: w.category()
        Category of sequences in Rational Field

    This is also the parent of any sequence::

        sage: w.parent()
        Category of sequences in Rational Field

    The default universe for any sequence, if no compatible parent structure
    can be found, is the universe of all Sage objects.

    This example illustrates how every element of a list is taken into account
    when constructing a sequence.::

        sage: v = Sequence([1,7,6,GF(5)(3)]); v
        [1, 2, 1, 3]
        sage: v.universe()
        Finite Field of size 5
        sage: v.parent()
        Category of sequences in Finite Field of size 5
        sage: v.parent()([7,8,9])
        [2, 3, 4]
    """
    from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal


    if isinstance(x, Sequence_generic) and universe is None:
        universe = x.universe()
        x = list(x)

    if isinstance(x, MPolynomialIdeal) and universe is None:
        universe = x.ring()
        x = x.gens()

    if universe is None:
        if not isinstance(x, (list, tuple)):
            x = list(x)
            #raise TypeError("x must be a list or tuple")

        if len(x) == 0:
            import sage.categories.all
            universe = sage.categories.all.Objects()
        else:
            import sage.structure.element as coerce
            y = x
            x = list(x)   # make a copy, or we'd change the type of the elements of x, which would be bad.
            if use_sage_types:
                # convert any Python built-in numerical types to Sage objects
                from sage.rings.integer_ring import ZZ
                from sage.rings.real_double import RDF
                from sage.rings.complex_double import CDF
                for i in range(len(x)):
                    if isinstance(x[i], int) or isinstance(x[i], long):
                        x[i] = ZZ(x[i])
                    elif isinstance(x[i], float):
                        x[i] = RDF(x[i])
                    elif isinstance(x[i], complex):
                        x[i] = CDF(x[i])
            # start the pairwise coercion
            for i in range(len(x)-1):
                try:
                    x[i], x[i+1] = coerce.canonical_coercion(x[i],x[i+1])
                except TypeError:
                    import sage.categories.all
                    universe = sage.categories.all.Objects()
                    x = list(y)
                    check = False  # no point
                    break
            if universe is None:   # no type errors raised.
                universe = coerce.parent(x[len(x)-1])

    from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing
    from sage.rings.quotient_ring import is_QuotientRing
    from sage.rings.polynomial.pbori import BooleanMonomialMonoid

    if is_MPolynomialRing(universe) or \
            (is_QuotientRing(universe) and is_MPolynomialRing(universe.cover_ring())) or \
            isinstance(universe, BooleanMonomialMonoid):
        from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
        try:
            return PolynomialSequence(x, universe, immutable=immutable, cr=cr, cr_str=cr_str)
        except (TypeError,AttributeError):
            return Sequence_generic(x, universe, check, immutable, cr, cr_str, use_sage_types)
    else:
        return Sequence_generic(x, universe, check, immutable, cr, cr_str, use_sage_types)
Ejemplo n.º 4
0
def Sequence(x, universe=None, check=True, immutable=False, cr=False, cr_str=None, use_sage_types=False):
    """
    A mutable list of elements with a common guaranteed universe,
    which can be set immutable.

    A universe is either an object that supports coercion (e.g., a
    parent), or a category.

    INPUT:

    - ``x`` - a list or tuple instance

    - ``universe`` - (default: None) the universe of elements; if None
      determined using canonical coercions and the entire list of
      elements.  If list is empty, is category Objects() of all
      objects.

    - ``check`` -- (default: True) whether to coerce the elements of x
      into the universe

    - ``immutable`` - (default: True) whether or not this sequence is
      immutable

    - ``cr`` - (default: False) if True, then print a carriage return
      after each comma when printing this sequence.

    - ``cr_str`` - (default: False) if True, then print a carriage return
      after each comma when calling ``str()`` on this sequence.

    - ``use_sage_types`` -- (default: False) if True, coerce the
       built-in Python numerical types int, long, float, complex to the
       corresponding Sage types (this makes functions like vector()
       more flexible)
    
    OUTPUT:

    - a sequence

    EXAMPLES::
    
        sage: v = Sequence(range(10))
        sage: v.universe()
        <type 'int'>
        sage: v
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    We can request that the built-in Python numerical types be coerced
    to Sage objects::
    
        sage: v = Sequence(range(10), use_sage_types=True)
        sage: v.universe()
        Integer Ring
        sage: v
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    You can also use seq for "Sequence", which is identical to using
    Sequence::
    
        sage: v = seq([1,2,1/1]); v
        [1, 2, 1]
        sage: v.universe()
        Rational Field
        sage: v.parent()
        Category of sequences in Rational Field
        sage: v.parent()([3,4/3])
        [3, 4/3]
        

    Note that assignment coerces if possible,::
    
        sage: v = Sequence(range(10), ZZ)
        sage: a = QQ(5)
        sage: v[3] = a
        sage: parent(v[3])
        Integer Ring
        sage: parent(a)
        Rational Field
        sage: v[3] = 2/3
        Traceback (most recent call last):
        ...
        TypeError: no conversion of this rational to integer

    Sequences can be used absolutely anywhere lists or tuples can be used::
    
        sage: isinstance(v, list)
        True

    Sequence can be immutable, so entries can't be changed::
    
        sage: v = Sequence([1,2,3], immutable=True)
        sage: v.is_immutable()
        True
        sage: v[0] = 5
        Traceback (most recent call last):
        ...
        ValueError: object is immutable; please change a copy instead.

    Only immutable sequences are hashable (unlike Python lists),
    though the hashing is potentially slow, since it first involves
    conversion of the sequence to a tuple, and returning the hash of
    that.::

        sage: v = Sequence(range(10), ZZ, immutable=True)
        sage: hash(v)
        1591723448             # 32-bit
        -4181190870548101704   # 64-bit


    If you really know what you are doing, you can circumvent the type
    checking (for an efficiency gain)::
    
        sage: list.__setitem__(v, int(1), 2/3)        # bad circumvention
        sage: v
        [0, 2/3, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: list.__setitem__(v, int(1), int(2))     # not so bad circumvention

    You can make a sequence with a new universe from an old sequence.::
    
        sage: w = Sequence(v, QQ)
        sage: w
        [0, 2, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: w.universe()
        Rational Field
        sage: w[1] = 2/3
        sage: w
        [0, 2/3, 2, 3, 4, 5, 6, 7, 8, 9]

    Sequences themselves live in a category, the category of all sequences
    in the given universe.::
    
        sage: w.category()
        Category of sequences in Rational Field
        
    This is also the parent of any sequence::
    
        sage: w.parent()
        Category of sequences in Rational Field

    The default universe for any sequence, if no compatible parent structure
    can be found, is the universe of all Sage objects.

    This example illustrates how every element of a list is taken into account
    when constructing a sequence.::
    
        sage: v = Sequence([1,7,6,GF(5)(3)]); v
        [1, 2, 1, 3]
        sage: v.universe()
        Finite Field of size 5
        sage: v.parent()
        Category of sequences in Finite Field of size 5
        sage: v.parent()([7,8,9])
        [2, 3, 4]
    """
    from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal


    if isinstance(x, Sequence_generic) and universe is None:
        universe = x.universe()
        x = list(x)

    if isinstance(x, MPolynomialIdeal) and universe is None:
        universe = x.ring()
        x = x.gens()

    if universe is None:
        if not isinstance(x, (list, tuple)):
            x = list(x)
            #raise TypeError("x must be a list or tuple")

        if len(x) == 0:
            import sage.categories.all
            universe = sage.categories.all.Objects()
        else:
            import sage.structure.element as coerce
            y = x
            x = list(x)   # make a copy, or we'd change the type of the elements of x, which would be bad.
            if use_sage_types:
                # convert any Python built-in numerical types to Sage objects
                from sage.rings.integer_ring import ZZ
                from sage.rings.real_double import RDF
                from sage.rings.complex_double import CDF
                for i in range(len(x)):
                    if isinstance(x[i], int) or isinstance(x[i], long):
                        x[i] = ZZ(x[i])
                    elif isinstance(x[i], float):
                        x[i] = RDF(x[i])
                    elif isinstance(x[i], complex):
                        x[i] = CDF(x[i])
            # start the pairwise coercion
            for i in range(len(x)-1):
                try:
                    x[i], x[i+1] = coerce.canonical_coercion(x[i],x[i+1])
                except TypeError:
                    import sage.categories.all
                    universe = sage.categories.all.Objects()
                    x = list(y)
                    check = False  # no point
                    break
            if universe is None:   # no type errors raised.
                universe = coerce.parent(x[len(x)-1])

    from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing
    from sage.rings.quotient_ring import is_QuotientRing
    from sage.rings.polynomial.pbori import BooleanMonomialMonoid

    if is_MPolynomialRing(universe) or \
            (is_QuotientRing(universe) and is_MPolynomialRing(universe.cover_ring())) or \
            isinstance(universe, BooleanMonomialMonoid):
        from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
        try:
            return PolynomialSequence(x, universe, immutable=immutable, cr=cr, cr_str=cr_str)
        except (TypeError,AttributeError):
            return Sequence_generic(x, universe, check, immutable, cr, cr_str, use_sage_types)
    else:
        return Sequence_generic(x, universe, check, immutable, cr, cr_str, use_sage_types)
Ejemplo n.º 5
0
def normalize_post_transform(dop, post_transform):
    if post_transform is None:
        post_transform = dop.parent().one()
    else:
        _, post_transform = canonical_coercion(dop, post_transform)
    return post_transform % dop
Ejemplo n.º 6
0
Archivo: bch.py Proyecto: sagemath/sage
def bch_iterator(X=None, Y=None):
    r"""
    A generator function which returns successive terms of the
    Baker-Campbell-Hausdorff formula.

    INPUT:

    - ``X`` -- (optional) an element of a Lie algebra
    - ``Y`` -- (optional) an element of a Lie algebra

    The BCH formula is an expression for `\log(\exp(X)\exp(Y))` as a sum of Lie
    brackets of ``X`` and ``Y`` with rational coefficients. In arbitrary Lie
    algebras, the infinite sum is only guaranteed to converge for ``X`` and
    ``Y`` close to zero.

    If the elements ``X`` and ``Y`` are not given, then the iterator will
    return successive terms of the abstract BCH formula, i.e., the BCH formula
    for the generators of the free Lie algebra on 2 generators.

    If the Lie algebra containing ``X`` and ``Y`` is not nilpotent, the
    iterator will output infinitely many elements. If the Lie algebra is
    nilpotent, the number of elements outputted is equal to the nilpotency step.

    EXAMPLES:

    The terms of the abstract BCH formula up to fifth order brackets::

        sage: from sage.algebras.lie_algebras.bch import bch_iterator
        sage: bch = bch_iterator()
        sage: next(bch)
        X + Y
        sage: next(bch)
        1/2*[X, Y]
        sage: next(bch)
        1/12*[X, [X, Y]] + 1/12*[[X, Y], Y]
        sage: next(bch)
        1/24*[X, [[X, Y], Y]]
        sage: next(bch)
        -1/720*[X, [X, [X, [X, Y]]]] + 1/180*[X, [X, [[X, Y], Y]]]
        + 1/360*[[X, [X, Y]], [X, Y]] + 1/180*[X, [[[X, Y], Y], Y]]
        + 1/120*[[X, Y], [[X, Y], Y]] - 1/720*[[[[X, Y], Y], Y], Y]

    For nilpotent Lie algebras the BCH formula only has finitely many terms::

        sage: L = LieAlgebra(QQ, 2, step=3)
        sage: L.inject_variables()
        Defining X_1, X_2, X_12, X_112, X_122
        sage: [Z for Z in bch_iterator(X_1, X_2)]
        [X_1 + X_2, 1/2*X_12, 1/12*X_112 + 1/12*X_122]
        sage: [Z for Z in bch_iterator(X_1 + X_2, X_12)]
        [X_1 + X_2 + X_12, 1/2*X_112 - 1/2*X_122, 0]

    The elements ``X`` and ``Y`` don't need to be elements of the same Lie
    algebra if there is a coercion from one to the other::

        sage: L = LieAlgebra(QQ, 3, step=2)
        sage: L.inject_variables()
        Defining X_1, X_2, X_3, X_12, X_13, X_23
        sage: S = L.subalgebra(X_1, X_2)
        sage: bch1 = [Z for Z in bch_iterator(S(X_1), S(X_2))]; bch1
        [X_1 + X_2, 1/2*X_12]
        sage: bch1[0].parent() == S
        True
        sage: bch2 = [Z for Z in bch_iterator(S(X_1), X_3)]; bch2
        [X_1 + X_3, 1/2*X_13]
        sage: bch2[0].parent() == L
        True

    The BCH formula requires a coercion from the rationals::

        sage: L.<X,Y,Z> = LieAlgebra(ZZ, 2, step=2)
        sage: bch = bch_iterator(X, Y); next(bch)
        Traceback (most recent call last):
        ...
        TypeError: the BCH formula is not well defined since Integer Ring has no coercion from Rational Field

    TESTS:

    Compare to the BCH formula up to degree 5 given by wikipedia::

        sage: from sage.algebras.lie_algebras.bch import bch_iterator
        sage: bch = bch_iterator()
        sage: L.<X,Y> = LieAlgebra(QQ)
        sage: L = L.Lyndon()
        sage: computed_BCH = L.sum(next(bch) for k in range(5))
        sage: wikiBCH = X + Y + 1/2*L[X,Y] + 1/12*(L[X,[X,Y]] + L[Y,[Y,X]])
        sage: wikiBCH += -1/24*L[Y,[X,[X,Y]]]
        sage: wikiBCH += -1/720*(L[Y,[Y,[Y,[Y,X]]]] + L[X,[X,[X,[X,Y]]]])
        sage: wikiBCH += 1/360*(L[X,[Y,[Y,[Y,X]]]] + L[Y,[X,[X,[X,Y]]]])
        sage: wikiBCH += 1/120*(L[Y,[X,[Y,[X,Y]]]] + L[X,[Y,[X,[Y,X]]]])
        sage: computed_BCH == wikiBCH
        True

    ALGORITHM:

    The BCH formula `\log(\exp(X)\exp(Y)) = \sum_k Z_k` is computed starting
    from `Z_1 = X + Y`, by the recursion

    .. MATH::

        (m+1)Z_{m+1} =  \frac{1}{2}[X - Y, Z_m]
        + \sum_{2\leq 2p \leq m}\frac{B_{2p}}{(2p)!}\sum_{k_1+\cdots+k_{2p}=m}
        [Z_{k_1}, [\cdots [Z_{k_{2p}}, X + Y]\cdots],

    where `B_{2p}` are the Bernoulli numbers, see Lemma 2.15.3. in [Var1984]_.

    .. WARNING::

        The time needed to compute each successive term increases exponentially.
        For example on one machine iterating through `Z_{11},...,Z_{18}` for a
        free Lie algebra, computing each successive term took 4-5 times longer,
        going from 0.1s for `Z_{11}` to 21 minutes for `Z_{18}`.
    """
    if X is None or Y is None:
        L = LieAlgebra(QQ, ['X', 'Y']).Lyndon()
        X, Y = L.lie_algebra_generators()
    else:
        X, Y = canonical_coercion(X, Y)
        L = X.parent()

    R = L.base_ring()
    if not R.has_coerce_map_from(QQ):
        raise TypeError("the BCH formula is not well defined since %s "
                        "has no coercion from %s" % (R, QQ))

    xdif = X - Y
    Z = [0, X + Y]  # 1-based indexing for convenience
    m = 1
    yield Z[1]

    while True:
        m += 1
        if L in LieAlgebras.Nilpotent and m > L.step():
            return

        # apply the recursion formula of [Var1984]
        Zm = ~QQ(2 * m) * xdif.bracket(Z[-1])
        for p in range(1, (m - 1) // 2 + 1):
            partitions = IntegerListsLex(m - 1, length=2 * p, min_part=1)
            coeff = bernoulli(2 * p) / QQ(m * factorial(2 * p))
            for kvec in partitions:
                W = Z[1]
                for k in kvec:
                    W = Z[k].bracket(W)
                Zm += coeff * W

        Z.append(Zm)
        yield Zm
Ejemplo n.º 7
0
def bch_iterator(X=None, Y=None):
    r"""
    A generator function which returns successive terms of the
    Baker-Campbell-Hausdorff formula.

    INPUT:

    - ``X`` -- (optional) an element of a Lie algebra
    - ``Y`` -- (optional) an element of a Lie algebra

    The BCH formula is an expression for `\log(\exp(X)\exp(Y))` as a sum of Lie
    brackets of ``X`` and ``Y`` with rational coefficients. In arbitrary Lie
    algebras, the infinite sum is only guaranteed to converge for ``X`` and
    ``Y`` close to zero.

    If the elements ``X`` and ``Y`` are not given, then the iterator will
    return successive terms of the abstract BCH formula, i.e., the BCH formula
    for the generators of the free Lie algebra on 2 generators.

    If the Lie algebra containing ``X`` and ``Y`` is not nilpotent, the
    iterator will output infinitely many elements. If the Lie algebra is
    nilpotent, the number of elements outputted is equal to the nilpotency step.

    EXAMPLES:

    The terms of the abstract BCH formula up to fifth order brackets::

        sage: from sage.algebras.lie_algebras.bch import bch_iterator
        sage: bch = bch_iterator()
        sage: next(bch)
        X + Y
        sage: next(bch)
        1/2*[X, Y]
        sage: next(bch)
        1/12*[X, [X, Y]] + 1/12*[[X, Y], Y]
        sage: next(bch)
        1/24*[X, [[X, Y], Y]]
        sage: next(bch)
        -1/720*[X, [X, [X, [X, Y]]]] + 1/180*[X, [X, [[X, Y], Y]]]
        + 1/360*[[X, [X, Y]], [X, Y]] + 1/180*[X, [[[X, Y], Y], Y]]
        + 1/120*[[X, Y], [[X, Y], Y]] - 1/720*[[[[X, Y], Y], Y], Y]

    For nilpotent Lie algebras the BCH formula only has finitely many terms::

        sage: L = LieAlgebra(QQ, 2, step=3)
        sage: L.inject_variables()
        Defining X_1, X_2, X_12, X_112, X_122
        sage: [Z for Z in bch_iterator(X_1, X_2)]
        [X_1 + X_2, 1/2*X_12, 1/12*X_112 + 1/12*X_122]
        sage: [Z for Z in bch_iterator(X_1 + X_2, X_12)]
        [X_1 + X_2 + X_12, 1/2*X_112 - 1/2*X_122, 0]

    The elements ``X`` and ``Y`` don't need to be elements of the same Lie
    algebra if there is a coercion from one to the other::

        sage: L = LieAlgebra(QQ, 3, step=2)
        sage: L.inject_variables()
        Defining X_1, X_2, X_3, X_12, X_13, X_23
        sage: S = L.subalgebra(X_1, X_2)
        sage: bch1 = [Z for Z in bch_iterator(S(X_1), S(X_2))]; bch1
        [X_1 + X_2, 1/2*X_12]
        sage: bch1[0].parent() == S
        True
        sage: bch2 = [Z for Z in bch_iterator(S(X_1), X_3)]; bch2
        [X_1 + X_3, 1/2*X_13]
        sage: bch2[0].parent() == L
        True

    The BCH formula requires a coercion from the rationals::

        sage: L.<X,Y,Z> = LieAlgebra(ZZ, 2, step=2)
        sage: bch = bch_iterator(X, Y); next(bch)
        Traceback (most recent call last):
        ...
        TypeError: the BCH formula is not well defined since Integer Ring has no coercion from Rational Field

    TESTS:

    Compare to the BCH formula up to degree 5 given by wikipedia::

        sage: from sage.algebras.lie_algebras.bch import bch_iterator
        sage: bch = bch_iterator()
        sage: L.<X,Y> = LieAlgebra(QQ)
        sage: L = L.Lyndon()
        sage: computed_BCH = L.sum(next(bch) for k in range(5))
        sage: wikiBCH = X + Y + 1/2*L[X,Y] + 1/12*(L[X,[X,Y]] + L[Y,[Y,X]])
        sage: wikiBCH += -1/24*L[Y,[X,[X,Y]]]
        sage: wikiBCH += -1/720*(L[Y,[Y,[Y,[Y,X]]]] + L[X,[X,[X,[X,Y]]]])
        sage: wikiBCH += 1/360*(L[X,[Y,[Y,[Y,X]]]] + L[Y,[X,[X,[X,Y]]]])
        sage: wikiBCH += 1/120*(L[Y,[X,[Y,[X,Y]]]] + L[X,[Y,[X,[Y,X]]]])
        sage: computed_BCH == wikiBCH
        True

    ALGORITHM:

    The BCH formula `\log(\exp(X)\exp(Y)) = \sum_k Z_k` is computed starting
    from `Z_1 = X + Y`, by the recursion

    .. MATH::

        (m+1)Z_{m+1} =  \frac{1}{2}[X - Y, Z_m]
        + \sum_{2\leq 2p \leq m}\frac{B_{2p}}{(2p)!}\sum_{k_1+\cdots+k_{2p}=m}
        [Z_{k_1}, [\cdots [Z_{k_{2p}}, X + Y]\cdots],

    where `B_{2p}` are the Bernoulli numbers, see Lemma 2.15.3. in [Var1984]_.

    .. WARNING::

        The time needed to compute each successive term increases exponentially.
        For example on one machine iterating through `Z_{11},...,Z_{18}` for a
        free Lie algebra, computing each successive term took 4-5 times longer,
        going from 0.1s for `Z_{11}` to 21 minutes for `Z_{18}`.
    """
    if X is None or Y is None:
        L = LieAlgebra(QQ, ['X', 'Y']).Lyndon()
        X, Y = L.lie_algebra_generators()
    else:
        X, Y = canonical_coercion(X, Y)
        L = X.parent()

    R = L.base_ring()
    if not R.has_coerce_map_from(QQ):
        raise TypeError("the BCH formula is not well defined since %s "
                        "has no coercion from %s" % (R, QQ))

    xdif = X - Y
    Z = [0, X + Y]  # 1-based indexing for convenience
    m = 1
    yield Z[1]

    while True:
        m += 1
        if L in LieAlgebras.Nilpotent and m > L.step():
            return

        # apply the recursion formula of [Var1984]
        Zm = ~QQ(2 * m) * xdif.bracket(Z[-1])
        for p in range(1, (m - 1) // 2 + 1):
            partitions = IntegerListsLex(m - 1, length=2 * p, min_part=1)
            coeff = bernoulli(2 * p) / QQ(m * factorial(2 * p))
            for kvec in partitions:
                W = Z[1]
                for k in kvec:
                    W = Z[k].bracket(W)
                Zm += coeff * W

        Z.append(Zm)
        yield Zm
Ejemplo n.º 8
0
    def __init__(self,
                 x,
                 universe=None,
                 check=True,
                 immutable=False,
                 cr=False,
                 cr_str=None,
                 use_sage_types=False):
        """
        Create a sequence.

        EXAMPLES::

            sage: Sequence([1..5])
            [1, 2, 3, 4, 5]
            sage: a = Sequence([1..3], universe=QQ, check=False, immutable=True, cr=True, cr_str=False, use_sage_types=True)
            sage: a
            [
            1,
            2,
            3
            ]
            sage: a = Sequence([1..5], universe=QQ, check=False, immutable=True, cr_str=True, use_sage_types=True)
            sage: a
            [1, 2, 3, 4, 5]
            sage: a._Sequence__cr_str
            True
            sage: a.__str__()
            '[\n1,\n2,\n3,\n4,\n5\n]'
        """
        if not isinstance(x, (list, tuple)):
            x = list(x)
            #raise TypeError, "x must be a list or tuple"
        self.__hash = None

        self.__cr = cr
        if cr_str is None:
            self.__cr_str = cr
        else:
            self.__cr_str = cr_str

        if isinstance(x, Sequence):
            if universe is None or universe == x.__universe:
                list.__init__(self, x)
                self.__universe = x.__universe
                self._is_immutable = immutable
                return
        if universe is None:
            if len(x) == 0:
                import sage.categories.all
                universe = sage.categories.all.Objects()
            else:
                import sage.structure.element as coerce
                y = x
                x = list(
                    x
                )  # make a copy, or we'd change the type of the elements of x, which would be bad.
                if use_sage_types:
                    # convert any Python builtin numerical types to Sage objects
                    from sage.rings.integer_ring import ZZ
                    from sage.rings.real_double import RDF
                    from sage.rings.complex_double import CDF
                    for i in range(len(x)):
                        if isinstance(x[i], int) or isinstance(x[i], long):
                            x[i] = ZZ(x[i])
                        elif isinstance(x[i], float):
                            x[i] = RDF(x[i])
                        elif isinstance(x[i], complex):
                            x[i] = CDF(x[i])
                # start the pairwise coercion
                for i in range(len(x) - 1):
                    try:
                        x[i], x[i + 1] = coerce.canonical_coercion(
                            x[i], x[i + 1])
                    except TypeError:
                        import sage.categories.all
                        universe = sage.categories.all.Objects()
                        x = list(y)
                        check = False  # no point
                        break
                if universe is None:  # no type errors raised.
                    universe = coerce.parent(x[len(x) - 1])
                #universe = sage.structure.coerce.parent(x[0])
        self.__universe = universe
        if check:
            x = [universe(t) for t in x]
        list.__init__(self, x)
        self._is_immutable = immutable