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
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
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)
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
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
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