def weight_distribution(self): r""" Returns the list whose `i`'th entry is the number of words of weight `i` in ``self``. Computing the weight distribution for a GRS code is very fast. Note that for random linear codes, it is computationally hard. EXAMPLES:: sage: F = GF(11) sage: n, k = 10, 5 sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) sage: C.weight_distribution() [1, 0, 0, 0, 0, 0, 2100, 6000, 29250, 61500, 62200] """ d = self.minimum_distance() n = self.length() q = self.base_ring().order() s = var('s') wd = [1] + [0] * (d - 1) for i in range(d, n + 1): tmp = binomial(n, i) * (q - 1) wd.append(tmp * symbolic_sum( binomial(i - 1, s) * (-1)**s * q**(i - d - s), s, 0, i - d)) return wd
def greedy_coefficient(self, d_vector, p, q): b = abs(self._B0[0, 1]) c = abs(self._B0[1, 0]) a1 = d_vector[0] a2 = d_vector[1] p = Integer(p) q = Integer(q) if p == 0 and q == 0: return 1 sum1 = 0 for k in range(1, p + 1): bin = 0 if a2 - c * q + k - 1 >= k: bin = binomial(a2 - c * q + k - 1, k) sum1 += (-1)**(k - 1) * self.greedy_coefficient(d_vector, p - k, q) * bin sum2 = 0 for l in range(1, q + 1): bin = 0 if a1 - b * p + l - 1 >= l: bin = binomial(a1 - b * p + l - 1, l) sum2 += (-1)**(l - 1) * self.greedy_coefficient(d_vector, p, q - l) * bin #print "sum1=",sum1,"sum2=",sum2 return max(sum1, sum2)
def weight_distribution(self): r""" Returns the list whose `i`'th entry is the number of words of weight `i` in ``self``. Computing the weight distribution for a GRS code is very fast. Note that for random linear codes, it is computationally hard. EXAMPLES:: sage: F = GF(11) sage: n, k = 10, 5 sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) sage: C.weight_distribution() [1, 0, 0, 0, 0, 0, 2100, 6000, 29250, 61500, 62200] """ d = self.minimum_distance() n = self.length() q = self.base_ring().order() s = var('s') wd = [1] + [0] * (d - 1) for i in range(d, n+1): tmp = binomial(n, i) * (q - 1) wd.append(tmp * symbolic_sum(binomial(i-1, s) * (-1) ** s * q ** (i - d - s), s, 0, i-d)) return wd
def cardinality(self): if self._max_deg == Infinity: return Infinity else: n = self._poly_ring.ngens() k1, k2 = self._min_deg, self._max_deg return binomial(n + k2, n) - binomial(n + k1 - 1, n)
def _b_power_k_r(self, k, r): r""" An expression involving moebius inversion in the powersum generators. For a positive value of ``k``, this expression is .. MATH:: \sum_{j=0}^r (-1)^{r-j}k^j\binom{r,j} \left( \frac{1}{k} \sum_{d|k} \mu(d/k) p_d \right)_k. INPUT: - ``k``, ``r`` -- positive integers OUTPUT: - an expression in the powersum basis of the symmetric functions EXAMPLES:: sage: st = SymmetricFunctions(QQ).st() sage: st._b_power_k_r(1,1) -p[] + p[1] sage: st._b_power_k_r(2,2) p[] + 4*p[1] + p[1, 1] - 4*p[2] - 2*p[2, 1] + p[2, 2] sage: st._b_power_k_r(3,2) p[] + 5*p[1] + p[1, 1] - 5*p[3] - 2*p[3, 1] + p[3, 3] """ p = self._p return p.sum( (-1)**(r-j) * k**j * binomial(r,j) * p.prod(self._b_power_k(k) - i*p.one() for i in range(j)) for j in range(r+1) )
def coproduct_on_basis(self, i): """ The coproduct of a basis element. .. MATH:: \Delta(P_i) = \sum_{j=0}^i P_{i-j} \otimes P_j INPUT: - ``i`` -- a non-negative integer OUTPUT: - an element of the tensor square of ``self`` TESTS:: sage: H = GradedHopfAlgebrasWithBasis(QQ).Connected().example() sage: H.monomial(3).coproduct() P0 # P3 + 3*P1 # P2 + 3*P2 # P1 + P3 # P0 """ return self.sum_of_terms( ((i-j, j), binomial(i, j)) for j in range(i+1) )
def coproduct_on_basis(self, i): """ The coproduct of a basis element. .. MATH:: \Delta(P_i) = \sum_{j=0}^i P_{i-j} \otimes P_j INPUT: - ``i`` -- a non-negative integer OUTPUT: - an element of the tensor square of ``self`` TESTS:: sage: H = GradedHopfAlgebrasWithBasis(QQ).Connected().example() sage: H.monomial(3).coproduct() P0 # P3 + 3*P1 # P2 + 3*P2 # P1 + P3 # P0 """ return self.sum_of_terms( ((i - j, j), binomial(i, j)) for j in range(i + 1))
def __init__(self, fmodule, degree, name=None, latex_name=None): r""" TESTS:: sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerFreeModule sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: A = ExtPowerFreeModule(M, 2) ; A 2nd exterior power of the Rank-3 free module M over the Integer Ring sage: TestSuite(A).run() """ from sage.functions.other import binomial self._fmodule = fmodule self._degree = ZZ(degree) rank = binomial(fmodule._rank, degree) if name is None and fmodule._name is not None: name = r'/\^{}('.format(degree) + fmodule._name + ')' if latex_name is None and fmodule._latex_name is not None: latex_name = r'\Lambda^{' + str(degree) + r'}\left(' + \ fmodule._latex_name + r'\right)' FiniteRankFreeModule.__init__( self, fmodule._ring, rank, name=name, latex_name=latex_name, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) fmodule._all_modules.add(self)
def _b_power_k_r(self, k, r): r""" An expression involving Moebius inversion in the powersum generators. For a positive value of ``k``, this expression is .. MATH:: \sum_{j=0}^r (-1)^{r-j}k^j\binom{r,j} \left( \frac{1}{k} \sum_{d|k} \mu(d/k) p_d \right)_k. INPUT: - ``k``, ``r`` -- positive integers OUTPUT: - an expression in the powersum basis of the symmetric functions EXAMPLES:: sage: st = SymmetricFunctions(QQ).st() sage: st._b_power_k_r(1,1) -p[] + p[1] sage: st._b_power_k_r(2,2) p[] + 4*p[1] + p[1, 1] - 4*p[2] - 2*p[2, 1] + p[2, 2] sage: st._b_power_k_r(3,2) p[] + 5*p[1] + p[1, 1] - 5*p[3] - 2*p[3, 1] + p[3, 3] """ p = self._p return p.sum( (-1)**(r - j) * k**j * binomial(r, j) * p.prod(self._b_power_k(k) - i * p.one() for i in range(j)) for j in range(r + 1))
def __init__(self, fmodule, degree, name=None, latex_name=None): r""" TESTS:: sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerFreeModule sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: A = ExtPowerFreeModule(M, 2) ; A 2nd exterior power of the Rank-3 free module M over the Integer Ring sage: TestSuite(A).run() """ from sage.functions.other import binomial self._fmodule = fmodule self._degree = degree rank = binomial(fmodule._rank, degree) if name is None and fmodule._name is not None: name = r'/\^{}('.format(degree) + fmodule._name + ')' if latex_name is None and fmodule._latex_name is not None: latex_name = r'\Lambda^{' + str(degree) + r'}\left(' + \ fmodule._latex_name + r'\right)' FiniteRankFreeModule.__init__(self, fmodule._ring, rank, name=name, latex_name=latex_name, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) # Unique representation: if self._degree == 1 or \ self._degree in self._fmodule._exterior_powers: raise ValueError("the {}th exterior power of ".format(degree) + "{}".format(self._fmodule) + " has already been created") else: self._fmodule._exterior_powers[self._degree] = self
def f(partition): n = 0 m = 1 for part in partition: n += part m *= q**binomial(part, 2) / q_factorial(part, q=q) return t**n * m
def cx_n_a(self, n, a, c=1): if n == 0: return OrePolynomial1(self._parent, [c * a]) res = OrePolynomial1(self._parent, [c * a]).px_n(n) for i in range(1, n + 1): a = self._parent._derivation(a) temp = OrePolynomial1(self._parent, [c * binomial(n, i) * a]).px_n(n - i) res = res + temp return res
def eqs_affine(x0, y0): r""" Make equation for the affine point x0, y0. Return a list of equations, each equation being a list of coefficients corresponding to the monomials in ``monomials``. """ eqs = [] for i in range(0, s): for j in range(0, s - i): eq = dict() for monomial in monomials: ihat = monomial[0] jhat = monomial[1] if ihat >= i and jhat >= j: icoeff = binomial(ihat, i) * x0**(ihat-i) \ if ihat > i else 1 jcoeff = binomial(jhat, j) * y0**(jhat-j) \ if jhat > j else 1 eq[monomial] = jcoeff * icoeff eqs.append([eq.get(monomial, 0) for monomial in monomials]) return eqs
def cardinality(self): r""" Return the cardinality of ``self``. EXAMPLES:: sage: from sage.combinat.blob_algebra import BlobDiagrams sage: BD4 = BlobDiagrams(4) sage: BD4.cardinality() 70 """ return binomial(2*self._n, self._n)
def eqs_affine(x0, y0): r""" Make equation for the affine point x0, y0. Return a list of equations, each equation being a list of coefficients corresponding to the monomials in ``monomials``. """ eqs = [] for i in range(s): for j in range(s - i): eq = dict() for monomial in monomials: ihat = monomial[0] jhat = monomial[1] if ihat >= i and jhat >= j: icoeff = binomial(ihat, i) * x0**(ihat-i) \ if ihat > i else 1 jcoeff = binomial(jhat, j) * y0**(jhat-j) \ if jhat > j else 1 eq[monomial] = jcoeff * icoeff eqs.append([eq.get(monomial, 0) for monomial in monomials]) return eqs
def __init__(self, fmodule, degree, name=None, latex_name=None): r""" TESTS:: sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: A = ExtPowerDualFreeModule(M, 2) ; A 2nd exterior power of the dual of the Rank-3 free module M over the Integer Ring sage: TestSuite(A).run() """ from sage.functions.other import binomial self._fmodule = fmodule self._degree = degree rank = binomial(fmodule._rank, degree) self._zero_element = 0 # provisory (to avoid infinite recursion in what # follows) if degree == 1: # case of the dual if name is None and fmodule._name is not None: name = fmodule._name + '*' if latex_name is None and fmodule._latex_name is not None: latex_name = fmodule._latex_name + r'^*' else: if name is None and fmodule._name is not None: name = r'/\^{}('.format(degree) + fmodule._name + '*)' if latex_name is None and fmodule._latex_name is not None: latex_name = r'\Lambda^{' + str(degree) + r'}\left(' + \ fmodule._latex_name + r'^*\right)' FiniteRankFreeModule.__init__( self, fmodule._ring, rank, name=name, latex_name=latex_name, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) # Unique representation: if self._degree in self._fmodule._dual_exterior_powers: raise ValueError("the {}th exterior power of ".format(degree) + "the dual of {}".format(self._fmodule) + " has already been created") else: self._fmodule._dual_exterior_powers[self._degree] = self # Zero element self._zero_element = self._element_constructor_(name='zero', latex_name='0') for basis in self._fmodule._known_bases: self._zero_element._components[basis] = \ self._zero_element._new_comp(basis)
def greedy_coefficient(self,d_vector,p,q): b = abs(self._B0[0,1]) c = abs(self._B0[1,0]) a1 = d_vector[0] a2 = d_vector[1] p = Integer(p) q = Integer(q) if p == 0 and q == 0: return 1 sum1 = 0 for k in range(1,p+1): bin = 0 if a2-c*q+k-1 >= k: bin = binomial(a2-c*q+k-1,k) sum1 += (-1)**(k-1)*self.greedy_coefficient(d_vector,p-k,q)*bin sum2 = 0 for l in range(1,q+1): bin = 0 if a1-b*p+l-1 >= l: bin = binomial(a1-b*p+l-1,l) sum2 += (-1)**(l-1)*self.greedy_coefficient(d_vector,p,q-l)*bin #print "sum1=",sum1,"sum2=",sum2 return max(sum1,sum2)
def __init__(self, fmodule, degree, name=None, latex_name=None): r""" TESTS:: sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: A = ExtPowerDualFreeModule(M, 2) ; A 2nd exterior power of the dual of the Rank-3 free module M over the Integer Ring sage: TestSuite(A).run() """ from sage.functions.other import binomial self._fmodule = fmodule self._degree = degree rank = binomial(fmodule._rank, degree) self._zero_element = 0 # provisory (to avoid infinite recursion in what # follows) if degree == 1: # case of the dual if name is None and fmodule._name is not None: name = fmodule._name + '*' if latex_name is None and fmodule._latex_name is not None: latex_name = fmodule._latex_name + r'^*' else: if name is None and fmodule._name is not None: name = '/\^{}('.format(degree) + fmodule._name + '*)' if latex_name is None and fmodule._latex_name is not None: latex_name = r'\Lambda^{' + str(degree) + r'}\left(' + \ fmodule._latex_name + r'^*\right)' FiniteRankFreeModule.__init__(self, fmodule._ring, rank, name=name, latex_name=latex_name, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) # Unique representation: if self._degree in self._fmodule._dual_exterior_powers: raise ValueError("the {}th exterior power of ".format(degree) + "the dual of {}".format(self._fmodule) + " has already been created") else: self._fmodule._dual_exterior_powers[self._degree] = self # Zero element self._zero_element = self._element_constructor_(name='zero', latex_name='0') for basis in self._fmodule._known_bases: self._zero_element._components[basis] = \ self._zero_element._new_comp(basis)
def _real_or_imaginary_part_of_power_of_complex_number(n, start): """ Let z = x + y * I. If start = 0, return Re(z^n). If start = 1, return Im(z^n). The result is a sage symbolic expression in x and y with rational coefficients. """ # By binomial theorem, we have # # n n n n n-i i # (x + y * I) = sum ( ) * I * x * y # i = 0 i # # The real/imaginary part consists of all even/odd terms in the sum: return sum([ binomial(n, i) * (-1) ** (i/2) * var('x') ** (n - i) * var('y') ** i for i in range(start, n + 1, 2)])
def probability_of_exactly_t_errors(self, t): r""" Returns the probability ``self`` has to return exactly ``t`` errors. INPUT: - ``t`` -- an integer EXAMPLES:: sage: epsilon = 0.3 sage: Chan = channels.QarySymmetricChannel(GF(59)^50, epsilon) sage: Chan.probability_of_exactly_t_errors(15) 0.122346861835401 """ n = self.input_space().dimension() epsilon = self.error_probability() return binomial(n, t) * epsilon**t * (1-epsilon)**(n-t)
def __init__(self, base_field, order, num_of_var): r""" TESTS: Note that the order given cannot be greater than (q-1). An error is raised if that happens:: sage: from sage.coding.reed_muller_code import QAryReedMullerCode sage: C = QAryReedMullerCode(GF(3), 4, 4) Traceback (most recent call last): ... ValueError: The order must be less than 3 The order and the number of variable must be integers:: sage: C = QAryReedMullerCode(GF(3),1.1,4) Traceback (most recent call last): ... ValueError: The order of the code must be an integer The base_field parameter must be a finite field:: sage: C = QAryReedMullerCode(QQ,1,4) Traceback (most recent call last): ... ValueError: the input `base_field` must be a FiniteField """ # input sanitization if not (base_field in FiniteFields): raise ValueError("the input `base_field` must be a FiniteField") if not (isinstance(order, (Integer, int))): raise ValueError("The order of the code must be an integer") if not (isinstance(num_of_var, (Integer, int))): raise ValueError("The number of variables must be an integer") q = base_field.cardinality() if (order >= q): raise ValueError("The order must be less than %s" % q) super(QAryReedMullerCode, self).__init__(base_field, q**num_of_var, "EvaluationVector", "Syndrome") self._order = order self._num_of_var = num_of_var self._dimension = binomial(num_of_var + order, order)
def dimension(self): r""" Return the dimension of ``self``. The dimension of the Schur algebra `S_R(n, r)` is .. MATH:: \dim S_R(n,r) = \binom{n^2+r-1}{r}. EXAMPLES:: sage: S = SchurAlgebra(QQ, 4, 2) sage: S.dimension() 136 sage: S = SchurAlgebra(QQ, 2, 4) sage: S.dimension() 35 """ return binomial(self._n ** 2 + self._r - 1, self._r)
def dimension(self): r""" Return the dimension of ``self``. The dimension of the Schur algebra `S_R(n, r)` is .. MATH:: \dim S_R(n,r) = \binom{n^2+r-1}{r}. EXAMPLES:: sage: S = SchurAlgebra(QQ, 4, 2) sage: S.dimension() 136 sage: S = SchurAlgebra(QQ, 2, 4) sage: S.dimension() 35 """ return binomial(self._n**2 + self._r - 1, self._r)
def __init__(self, base_field, order, num_of_var): r""" TESTS: Note that the order given cannot be greater than (q-1). An error is raised if that happens:: sage: from sage.coding.reed_muller_code import QAryReedMullerCode sage: C = QAryReedMullerCode(GF(3), 4, 4) Traceback (most recent call last): ... ValueError: The order must be less than 3 The order and the number of variable must be integers:: sage: C = QAryReedMullerCode(GF(3),1.1,4) Traceback (most recent call last): ... ValueError: The order of the code must be an integer The base_field parameter must be a finite field:: sage: C = QAryReedMullerCode(QQ,1,4) Traceback (most recent call last): ... ValueError: the input `base_field` must be a FiniteField """ # input sanitization if not(base_field in FiniteFields): raise ValueError("the input `base_field` must be a FiniteField") if not(isinstance(order, (Integer, int))): raise ValueError("The order of the code must be an integer") if not(isinstance(num_of_var, (Integer, int))): raise ValueError("The number of variables must be an integer") q = base_field.cardinality() if (order >= q): raise ValueError("The order must be less than %s" % q) super(QAryReedMullerCode,self).__init__(base_field, q**num_of_var, "EvaluationVector", "Syndrome") self._order = order self._num_of_var = num_of_var self._dimension = binomial(num_of_var + order, order)
def roth_rec(Q, lam, k): r""" Recursion of the root finding: Q is the remaining poly, lam is the power of x whose coefficient we are to determine now, and k is the remaining precision to handle (if ``precision`` is given) """ if precision and k <= 0: solutions.append((Rx(g[:lam]), lam)) return (T, strip) = _strip_x_pows(Q) if precision: k = k - strip Ty = Rx([p[0] for p in T]) if Ty.is_zero() or (precision and k <= 0): if precision: solutions.append((Rx(g[:lam]), lam)) else: solutions.append(Rx(g[:lam])) return roots = Ty.roots(multiplicities=False) for gamma in roots: g[lam] = gamma if lam < maxd: # Construct T(y=x*y + gamma) ell = len(T) - 1 yc = [[ binomial(s, t) * x**t * gamma**(s - t) for t in range(s + 1) ] for s in range(ell + 1)] Tg = [] for t in range(ell + 1): Tg.append(sum(yc[s][t] * T[s] for s in range(t, ell + 1))) roth_rec(Tg, lam + 1, k) else: if precision: solutions.append((Rx(g[:lam + 1]), lam + 1)) elif sum(Q[t] * gamma**t for t in range(len(Q))).is_zero(): solutions.append(Rx(g[:lam + 1])) return
def eval_formula(self, n, x): """ Evaluate ``chebyshev_U`` using an explicit formula. See [ASHandbook]_ 227 (p. 782) for details on the recurions. See also [EffCheby]_ for the recursion formulas. INPUT: - ``n`` -- an integer - ``x`` -- a value to evaluate the polynomial at (this can be any ring element) EXAMPLES:: sage: chebyshev_U.eval_formula(10, x) 1024*x^10 - 2304*x^8 + 1792*x^6 - 560*x^4 + 60*x^2 - 1 sage: chebyshev_U.eval_formula(-2, x) -1 sage: chebyshev_U.eval_formula(-1, x) 0 sage: chebyshev_U.eval_formula(0, x) 1 sage: chebyshev_U.eval_formula(1, x) 2*x sage: chebyshev_U.eval_formula(2,0.1) == chebyshev_U._evalf_(2,0.1) True """ if n < -1: return -self.eval_formula(-n - 2, x) res = parent(x).zero() for j in xrange(0, n // 2 + 1): f = binomial(n - j, j) res += (-1)**j * (2 * x)**(n - 2 * j) * f return res
def roth_rec(Q, lam, k): r""" Recursion of the root finding: Q is the remaining poly, lam is the power of x whose coefficient we are to determine now, and k is the remaining precision to handle (if ``precision`` is given) """ if precision and k <= 0: solutions.append((Rx(g[:lam]), lam)) return (T, strip) = _strip_x_pows(Q) if precision: k = k - strip Ty = Rx([ p[0] for p in T ]) if Ty.is_zero() or (precision and k <= 0): if precision: solutions.append((Rx(g[:lam]), lam)) else: solutions.append(Rx(g[:lam])) return roots = Ty.roots(multiplicities=False) for gamma in roots: g[lam] = gamma if lam<maxd: # Construct T(y=x*y + gamma) ell = len(T)-1 yc = [[binomial(s, t) * x**t * gamma**(s-t) for t in range(s+1)] for s in range(ell+1)] Tg = [] for t in range(ell+1): Tg.append(sum(yc[s][t] * T[s] for s in range(t, ell+1))) roth_rec(Tg , lam+1, k) else: if precision: solutions.append((Rx(g[:lam+1]), lam+1)) elif sum( Q[t] * gamma**t for t in range(len(Q)) ).is_zero(): solutions.append(Rx(g[:lam+1])) return
def eval_formula(self, n, x): """ Evaluate ``chebyshev_U`` using an explicit formula. See [ASHandbook]_ 227 (p. 782) for details on the recurions. See also [EffCheby]_ for the recursion formulas. INPUT: - ``n`` -- an integer - ``x`` -- a value to evaluate the polynomial at (this can be any ring element) EXAMPLES:: sage: chebyshev_U.eval_formula(10, x) 1024*x^10 - 2304*x^8 + 1792*x^6 - 560*x^4 + 60*x^2 - 1 sage: chebyshev_U.eval_formula(-2, x) -1 sage: chebyshev_U.eval_formula(-1, x) 0 sage: chebyshev_U.eval_formula(0, x) 1 sage: chebyshev_U.eval_formula(1, x) 2*x sage: chebyshev_U.eval_formula(2,0.1) == chebyshev_U._evalf_(2,0.1) True """ if n < -1: return -self.eval_formula(-n - 2, x) res = parent(x).zero() for j in xrange(0, n // 2 + 1): f = binomial(n - j, j) res += (-1) ** j * (2 * x) ** (n - 2 * j) * f return res
def number_of_partial_injection(n, algorithm='binomial'): r""" Return the number of partial injections on an set of `n` elements defined on a subset of `k` elements for each `k` in `0, 1, ..., n`. INPUT: - ``n`` -- integer - ``algorithm`` -- string (default: ``'binomial'``), ``'binomial'`` or ``'recursive'``. When n>50, the binomial coefficient approach is faster (linear time vs quadratic time). OUTPUT: list .. NOTE:: The recursive code of this function was originally written by Vincent Delecroix (Nov 30, 2017) the day after a discussion with Pascal Weil and me at LaBRI. EXAMPLES:: sage: from slabbe import number_of_partial_injection sage: number_of_partial_injection(0) [1] sage: number_of_partial_injection(1) [1, 1] sage: number_of_partial_injection(2) [1, 4, 2] sage: number_of_partial_injection(3) [1, 9, 18, 6] sage: number_of_partial_injection(4) [1, 16, 72, 96, 24] sage: number_of_partial_injection(5) [1, 25, 200, 600, 600, 120] sage: number_of_partial_injection(6) [1, 36, 450, 2400, 5400, 4320, 720] sage: number_of_partial_injection(7) [1, 49, 882, 7350, 29400, 52920, 35280, 5040] sage: number_of_partial_injection(8) [1, 64, 1568, 18816, 117600, 376320, 564480, 322560, 40320] TESTS:: sage: number_of_partial_injection(8, algorithm='recursive') [1, 64, 1568, 18816, 117600, 376320, 564480, 322560, 40320] REFERENCE: https://oeis.org/A144084 """ if algorithm == 'binomial': return [binomial(n,k)**2*factorial(k) for k in range(n+1)] elif algorithm == 'recursive': L = [ZZ(1)] for t in range(1, n+1): L.append(t * L[-1]) for k in range(t-1, 0, -1): L[k] = (t * L[k]) // (t-k) + t * L[k-1] return L
def _compute_sw_spherical_harm(s, l, m, theta, phi, condon_shortley=True, numerical=None): r""" Compute the spin-weighted spherical harmonic of spin weight ``s`` and indices ``(l,m)`` as a callable symbolic expression in (theta,phi) INPUT: - ``s`` -- integer; the spin weight - ``l`` -- non-negative integer; the harmonic degree - ``m`` -- integer within the range ``[-l, l]``; the azimuthal number - ``theta`` -- colatitude angle - ``phi`` -- azimuthal angle - ``condon_shortley`` -- (default: ``True``) determines whether the Condon-Shortley phase of `(-1)^m` is taken into account (see below) - ``numerical`` -- (default: ``None``) determines whether a symbolic or a numerical computation of a given type is performed; allowed values are - ``None``: a symbolic computation is performed - ``RDF``: Sage's machine double precision floating-point numbers (``RealDoubleField``) - ``RealField(n)``, where ``n`` is a number of bits: Sage's floating-point numbers with an arbitrary precision; note that ``RR`` is a shortcut for ``RealField(53)``. - ``float``: Python's floating-point numbers OUTPUT: - `{}_s Y_l^m(\theta,\phi)` either - as a symbolic expression if ``numerical`` is ``None`` - or a pair of floating-point numbers, each of them being of the type corresponding to ``numerical`` and representing respectively the real and imaginary parts of `{}_s Y_l^m(\theta,\phi)` ALGORITHM: The spin-weighted spherical harmonic is evaluated according to Eq. (3.1) of J. N. Golberg et al., J. Math. Phys. **8**, 2155 (1967) [:doi:`10.1063/1.1705135`], with an extra `(-1)^m` factor (the so-called *Condon-Shortley phase*) if ``condon_shortley`` is ``True``, the actual formula being then the one given in :wikipedia:`Spin-weighted_spherical_harmonics#Calculating` TESTS:: sage: from kerrgeodesic_gw.spin_weighted_spherical_harm import _compute_sw_spherical_harm sage: theta, phi = var("theta phi") sage: _compute_sw_spherical_harm(-2, 2, 1, theta, phi) 1/4*(sqrt(5)*cos(theta) + sqrt(5))*e^(I*phi)*sin(theta)/sqrt(pi) """ if abs(s) > l: return ZZ(0) if abs(theta) < 1.e-6: # TODO: fix the treatment of small theta values if theta < 0: # possibly with exact formula for theta=0 theta = -1.e-6 # else: # theta = 1.e-6 # cott2 = cos(theta / 2) / sin(theta / 2) res = 0 for r in range(l - s + 1): res += (-1)**(l - r - s) * (binomial(l - s, r) * binomial( l + s, r + s - m) * cott2**(2 * r + s - m)) res *= sin(theta / 2)**(2 * l) ff = factorial(l + m) * factorial(l - m) * (2 * l + 1) / ( factorial(l + s) * factorial(l - s)) if numerical: pre = sqrt(numerical(ff) / numerical(pi)) / 2 else: pre = sqrt(ff) / (2 * sqrt(pi)) res *= pre if condon_shortley: res *= (-1)**m if numerical: return (numerical(res * cos(m * phi)), numerical(res * sin(m * phi))) # Symbolic case: res = res.simplify_full() res = res.reduce_trig() # get rid of cos(theta/2) and sin(theta/2) res = res.simplify_trig() # further trigonometric simplifications res *= exp(I * m * phi) return res
def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, precision=None, normalized=True): r""" Return the asymptotic expansion of the coefficients of an power series with specified pole and logarithmic singularity. More precisely, this extracts the `n`-th coefficient .. MATH:: [z^n] \left(\frac{1}{1-z/\zeta}\right)^\alpha \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)^\beta \left(\frac{1}{z/\zeta} \log \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)\right)^\delta (if ``normalized=True``, the default) or .. MATH:: [z^n] \left(\frac{1}{1-z/\zeta}\right)^\alpha \left(\log \frac{1}{1-z/\zeta}\right)^\beta \left(\log \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)\right)^\delta (if ``normalized=False``). INPUT: - ``var`` -- a string for the variable name. - ``zeta`` -- (default: `1`) the location of the singularity. - ``alpha`` -- (default: `0`) the pole order of the singularty. - ``beta`` -- (default: `0`) the order of the logarithmic singularity. - ``delta`` -- (default: `0`) the order of the log-log singularity. Not yet implemented for ``delta != 0``. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``normalized`` -- (default: ``True``) a boolean, see above. OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.SingularityAnalysis('n', alpha=1) 1 sage: asymptotic_expansions.SingularityAnalysis('n', alpha=2) n + 1 sage: asymptotic_expansions.SingularityAnalysis('n', alpha=3) 1/2*n^2 + 3/2*n + 1 sage: _.parent() Asymptotic Ring <n^ZZ> over Rational Field :: sage: asymptotic_expansions.SingularityAnalysis('n', alpha=-3/2, ....: precision=3) 3/4/sqrt(pi)*n^(-5/2) + 45/32/sqrt(pi)*n^(-7/2) + 1155/512/sqrt(pi)*n^(-9/2) + O(n^(-11/2)) sage: asymptotic_expansions.SingularityAnalysis('n', alpha=-1/2, ....: precision=3) -1/2/sqrt(pi)*n^(-3/2) - 3/16/sqrt(pi)*n^(-5/2) - 25/256/sqrt(pi)*n^(-7/2) + O(n^(-9/2)) sage: asymptotic_expansions.SingularityAnalysis('n', alpha=1/2, ....: precision=4) 1/sqrt(pi)*n^(-1/2) - 1/8/sqrt(pi)*n^(-3/2) + 1/128/sqrt(pi)*n^(-5/2) + 5/1024/sqrt(pi)*n^(-7/2) + O(n^(-9/2)) sage: _.parent() Asymptotic Ring <n^QQ> over Symbolic Constants Subring :: sage: S = SR.subring(rejecting_variables=('n',)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=S.var('a'), ....: precision=4).map_coefficients(lambda c: c.factor()) 1/gamma(a)*n^(a - 1) + (1/2*(a - 1)*a/gamma(a))*n^(a - 2) + (1/24*(3*a - 1)*(a - 1)*(a - 2)*a/gamma(a))*n^(a - 3) + (1/48*(a - 1)^2*(a - 2)*(a - 3)*a^2/gamma(a))*n^(a - 4) + O(n^(a - 5)) sage: _.parent() Asymptotic Ring <n^(Symbolic Subring rejecting the variable n)> over Symbolic Subring rejecting the variable n :: sage: ae = asymptotic_expansions.SingularityAnalysis('n', ....: alpha=1/2, beta=1, precision=4); ae 1/sqrt(pi)*n^(-1/2)*log(n) + ((euler_gamma + 2*log(2))/sqrt(pi))*n^(-1/2) - 5/8/sqrt(pi)*n^(-3/2)*log(n) + (1/8*(3*euler_gamma + 6*log(2) - 8)/sqrt(pi) - (euler_gamma + 2*log(2) - 2)/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) sage: n = ae.parent().gen() sage: ae.subs(n=n-1).map_coefficients(lambda x: x.canonicalize_radical()) 1/sqrt(pi)*n^(-1/2)*log(n) + ((euler_gamma + 2*log(2))/sqrt(pi))*n^(-1/2) - 1/8/sqrt(pi)*n^(-3/2)*log(n) + (-1/8*(euler_gamma + 2*log(2))/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) :: sage: asymptotic_expansions.SingularityAnalysis('n', ....: alpha=1, beta=1/2, precision=4) log(n)^(1/2) + 1/2*euler_gamma*log(n)^(-1/2) + (-1/8*euler_gamma^2 + 1/48*pi^2)*log(n)^(-3/2) + (1/16*euler_gamma^3 - 1/32*euler_gamma*pi^2 + 1/8*zeta(3))*log(n)^(-5/2) + O(log(n)^(-7/2)) :: sage: ae = asymptotic_expansions.SingularityAnalysis('n', ....: alpha=0, beta=2, precision=14) sage: n = ae.parent().gen() sage: ae.subs(n=n-2) 2*n^(-1)*log(n) + 2*euler_gamma*n^(-1) - n^(-2) - 1/6*n^(-3) + O(n^(-5)) :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=-1/2, beta=1, precision=2, normalized=False) -1/2/sqrt(pi)*n^(-3/2)*log(n) + (-1/2*(euler_gamma + 2*log(2) - 2)/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1/2, alpha=0, beta=1, precision=3, normalized=False) 2^n*n^(-1) + O(2^n*n^(-2)) ALGORITHM: See [FS2009]_ together with the `errata list <http://algo.inria.fr/flajolet/Publications/AnaCombi/errata.pdf>`_. REFERENCES: .. [FS2009] Philippe Flajolet and Robert Sedgewick, `Analytic combinatorics <http://algo.inria.fr/flajolet/Publications/AnaCombi/book.pdf>`_. Cambridge University Press, Cambridge, 2009. TESTS:: sage: ex = asymptotic_expansions.SingularityAnalysis('n', alpha=-1/2, ....: precision=4) sage: n = ex.parent().gen() sage: coefficients = ((1-x)^(1/2)).series( ....: x, 21).truncate().coefficients(x, sparse=False) sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda k: coefficients[k], [5, 10, 20]) [(5, 0.015778873294?), (10, 0.01498952777?), (20, 0.0146264622?)] sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=2) 1/2*n^2 + 3/2*n + O(1) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=3) 1/2*n^2 + 3/2*n + 1 sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=4) 1/2*n^2 + 3/2*n + 1 :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=0) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=-1) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'm', alpha=-1/2, precision=3) -1/2/sqrt(pi)*m^(-3/2) - 3/16/sqrt(pi)*m^(-5/2) - 25/256/sqrt(pi)*m^(-7/2) + O(m^(-9/2)) sage: _.parent() Asymptotic Ring <m^QQ> over Symbolic Constants Subring Location of the singularity:: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=2, precision=3) (1/2)^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=1/2, precision=3) 2^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=CyclotomicField(3).gen(), ....: precision=3) (-zeta3 - 1)^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=4, zeta=2, precision=3) 1/6*(1/2)^n*n^3 + (1/2)^n*n^2 + 11/6*(1/2)^n*n + O((1/2)^n) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=-1, zeta=2, precision=3) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1/2, zeta=2, precision=3) 1/sqrt(pi)*(1/2)^n*n^(-1/2) - 1/8/sqrt(pi)*(1/2)^n*n^(-3/2) + 1/128/sqrt(pi)*(1/2)^n*n^(-5/2) + O((1/2)^n*n^(-7/2)) The following tests correspond to Table VI.5 in [FS2009]_. :: sage: A.<n> = AsymptoticRing('n^QQ * log(n)^QQ', QQ) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=-1/2, beta=1, precision=2, ....: normalized=False) * (- sqrt(pi*n^3)) 1/2*log(n) + 1/2*euler_gamma + log(2) - 1 + O(n^(-1)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=0, beta=1, precision=3, ....: normalized=False) n^(-1) + O(n^(-2)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=0, beta=2, precision=14, ....: normalized=False) * n 2*log(n) + 2*euler_gamma - n^(-1) - 1/6*n^(-2) + O(n^(-4)) sage: (asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1/2, beta=1, precision=4, ....: normalized=False) * sqrt(pi*n)).\ ....: map_coefficients(lambda x: x.expand()) log(n) + euler_gamma + 2*log(2) - 1/8*n^(-1)*log(n) + (-1/8*euler_gamma - 1/4*log(2))*n^(-1) + O(n^(-2)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1, beta=1, precision=13, ....: normalized=False) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1, beta=2, precision=4, ....: normalized=False) log(n)^2 + 2*euler_gamma*log(n) + euler_gamma^2 - 1/6*pi^2 + O(n^(-1)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=3/2, beta=1, precision=3, ....: normalized=False) * sqrt(pi/n) 2*log(n) + 2*euler_gamma + 4*log(2) - 4 + 3/4*n^(-1)*log(n) + O(n^(-1)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=2, beta=1, precision=5, ....: normalized=False) n*log(n) + (euler_gamma - 1)*n + log(n) + euler_gamma + 1/2 + O(n^(-1)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=2, beta=2, precision=4, ....: normalized=False) / n log(n)^2 + (2*euler_gamma - 2)*log(n) - 2*euler_gamma + euler_gamma^2 - 1/6*pi^2 + 2 + n^(-1)*log(n)^2 + O(n^(-1)*log(n)) Be aware that the last result does *not* coincide with [FS2009]_, they do have a different error term. Checking parameters:: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, 1, 1/2, precision=0, normalized=False) Traceback (most recent call last): ... ValueError: beta and delta must be integers sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, 1, 1, 1/2, normalized=False) Traceback (most recent call last): ... ValueError: beta and delta must be integers :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=0, beta=0, delta=1, precision=3) Traceback (most recent call last): ... NotImplementedError: not implemented for delta!=0 """ from itertools import islice, count from asymptotic_ring import AsymptoticRing from growth_group import ExponentialGrowthGroup, \ MonomialGrowthGroup from sage.arith.all import falling_factorial from sage.categories.cartesian_product import cartesian_product from sage.functions.other import binomial, gamma from sage.calculus.calculus import limit from sage.misc.cachefunc import cached_function from sage.arith.srange import srange from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.symbolic.ring import SR SCR = SR.subring(no_variables=True) s = SR('s') iga = 1 / gamma(alpha) if iga.parent() is SR: try: iga = SCR(iga) except TypeError: pass coefficient_ring = iga.parent() if beta != 0: coefficient_ring = SCR @cached_function def inverse_gamma_derivative(shift, r): """ Return value of `r`-th derivative of 1/Gamma at alpha-shift. """ if r == 0: result = iga * falling_factorial(alpha - 1, shift) else: result = limit((1 / gamma(s)).diff(s, r), s=alpha - shift) try: return coefficient_ring(result) except TypeError: return result if isinstance(alpha, int): alpha = ZZ(alpha) if isinstance(beta, int): beta = ZZ(beta) if isinstance(delta, int): delta = ZZ(delta) if precision is None: precision = AsymptoticRing.__default_prec__ if not normalized and not (beta in ZZ and delta in ZZ): raise ValueError("beta and delta must be integers") if delta != 0: raise NotImplementedError("not implemented for delta!=0") groups = [] if zeta != 1: groups.append(ExponentialGrowthGroup((1 / zeta).parent(), var)) groups.append(MonomialGrowthGroup(alpha.parent(), var)) if beta != 0: groups.append( MonomialGrowthGroup(beta.parent(), 'log({})'.format(var))) group = cartesian_product(groups) A = AsymptoticRing(growth_group=group, coefficient_ring=coefficient_ring, default_prec=precision) n = A.gen() if zeta == 1: exponential_factor = 1 else: exponential_factor = n.rpow(1 / zeta) if beta in ZZ and beta >= 0: it = ((k, r) for k in count() for r in srange(beta + 1)) k_max = precision else: it = ((0, r) for r in count()) k_max = 0 if beta != 0: log_n = n.log() else: # avoid construction of log(n) # because it does not exist in growth group. log_n = 1 it = reversed(list(islice(it, precision + 1))) if normalized: beta_denominator = beta else: beta_denominator = 0 L = _sa_coefficients_lambda_(max(1, k_max), beta=beta_denominator) (k, r) = next(it) result = (n**(-k) * log_n**(-r)).O() if alpha in ZZ and beta == 0: if alpha > 0 and alpha <= precision: result = A(0) elif alpha <= 0 and precision > 0: from misc import NotImplementedOZero raise NotImplementedOZero(A) for (k, r) in it: result += binomial(beta, r) * \ sum(L[(k, ell)] * (-1)**ell * inverse_gamma_derivative(ell, r) for ell in srange(k, 2*k+1) if (k, ell) in L) * \ n**(-k) * log_n**(-r) result *= exponential_factor * n**(alpha - 1) * log_n**beta return result
def _test_jacobi(self, **options): """ Test the Jacobi axiom of this super Lie conformal algebra. INPUT: - ``options`` -- any keyword arguments acceptde by :meth:`_tester` EXAMPLES: By default, this method tests only the elements returned by ``self.some_elements()``:: sage: V = lie_conformal_algebras.Affine(QQ, 'B2') sage: V._test_jacobi() # long time (6 seconds) It works for super Lie conformal algebras too:: sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) sage: V._test_jacobi() We can use specific elements by passing the ``elements`` keyword argument:: sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) sage: V.inject_variables() Defining e, h, f, K sage: V._test_jacobi(elements=(e, 2*f+h, 3*h)) TESTS:: sage: wrongdict = {('a', 'a'): {0: {('b', 0): 1}}, ('b', 'a'): {0: {('a', 0): 1}}} sage: V = LieConformalAlgebra(QQ, wrongdict, names=('a', 'b'), parity=(1, 0)) sage: V._test_jacobi() Traceback (most recent call last): ... AssertionError: {(0, 0): -3*a} != {} - {(0, 0): -3*a} + {} """ tester = self._tester(**options) S = tester.some_elements() # Try our best to avoid non-homogeneous elements elements = [] for s in S: try: s.is_even_odd() except ValueError: try: elements.extend([s.even_component(), s.odd_component()]) except (AttributeError, ValueError): pass continue elements.append(s) S = elements from sage.misc.misc import some_tuples from sage.functions.other import binomial pz = tester._instance.zero() for x,y,z in some_tuples(S, 3, tester._max_runs): if x.is_zero() or y.is_zero(): sgn = 1 elif x.is_even_odd() * y.is_even_odd(): sgn = -1 else: sgn = 1 brxy = x.bracket(y) brxz = x.bracket(z) bryz = y.bracket(z) br1 = {k: x.bracket(v) for k,v in bryz.items()} br2 = {k: v.bracket(z) for k,v in brxy.items()} br3 = {k: y.bracket(v) for k,v in brxz.items()} jac1 = {(j,k): v for k in br1 for j,v in br1[k].items()} jac3 = {(k,j): v for k in br3 for j,v in br3[k].items()} jac2 = {} for k,br in br2.items(): for j,v in br.items(): for r in range(j+1): jac2[(k+r, j-r)] = (jac2.get((k+r, j-r), pz) + binomial(k+r, r)*v) for k,v in jac2.items(): jac1[k] = jac1.get(k, pz) - v for k,v in jac3.items(): jac1[k] = jac1.get(k, pz) - sgn*v jacobiator = {k: v for k,v in jac1.items() if v} tester.assertDictEqual(jacobiator, {})
def _frob_sparse(self, i, j, N0): r""" Compute `Frob(x^i y^(-j) dx ) / dx` for y^r = f(x) with N0 terms INPUT: - ``i`` - The power of x in the expression `Frob(x^i dx/y^j) / dx` - ``j`` - The (negative) power of y in the expression `Frob(x^i dx/y^j) / dx` OUTPUT: ``frobij`` - a Matrix of size (d * (N0 - 1) + ) x (N0) that represents the Frobenius expansion of x^i dx/y^j modulo p^(N0 + 1) the entry (l, s) corresponds to the coefficient associated to the monomial x**(p * (i + 1 + l) -1) * y**(p * -(j + r*s)) (l, s) --> (p * (i + 1 + l) -1, p * -(j + r*s)) ALGORITHM: Compute: Frob(x^i dx/y^j) / dx = p * x ** (p * (i+1) - 1) * y ** (-j*p) * Sigma where: .. MATH:: Sigma = \sum_{k = 0} ^{N0-1} \sum_{s = 0} ^k (-1) ** (k-s) * binomial(k, s) * binomial(-j/r, k) * self._frobpow[s] * self._y ** (-self._r * self._p * s) = \sum_{s = 0} ^{N0 - 1} \sum_{k = s} ^N0 (-1) ** (k-s) * binomial(k, s) * binomial(-j/self._r, k) * self._frobpow[s] * self._y ** (-self._r*self._p*s) = \sum_{s = 0} ^{N0-1} D_{j, s} * self._frobpow[s] * self._y ** (-self._r * self._p * s) = \sum_{s = 0} ^N0 \sum_{l = 0} ^(d*s) D_{j, s} * self._frobpow[s][l] * x ** (self._p ** l) * y ** (-self._r * self._p ** s) and: .. MATH:: D_{j, s} = \sum_{k = s} ^N0 (-1) ** (k-s) * binomial(k, s) * binomial(-j/self._r, k) ) TESTS:: sage: p = 499 sage: x = PolynomialRing(GF(p),"x").gen() sage: C = CyclicCover(3, x^4 + 4*x^3 + 9*x^2 + 3*x + 1) sage: C._init_frob() sage: C._frob_sparse(2, 0, 1) [499] sage: C._frob_sparse(2, 0, 2) [499 0] [ 0 0] [ 0 0] [ 0 0] [ 0 0] sage: C._frob_sparse(2, 1, 1) [499] sage: C._frob_sparse(2, 1, 2) [ 82834998 41417000] [ 0 124251000] [ 0 124250002] [ 0 41416501] [ 0 41417000] sage: C._frob_sparse(2, 2, 1) [499] """ def _extend_frobpow(power): if power < len(self._frobpow_list): pass else: frobpow = self._Zqx(self._frobpow_list[-1]) for k in range(len(self._frobpow_list), power + 1): frobpow *= self._frobf self._frobpow_list.extend([frobpow.list()]) assert power < len(self._frobpow_list) _extend_frobpow(N0) r = self._r Dj = [ self._Zq( sum([(-1)**(k - l) * binomial(k, l) * binomial(-ZZ(j) / r, k) for k in range(l, N0)])) for l in range(N0) ] frobij = matrix(self._Zq, self._d * (N0 - 1) + 1, N0) for s in range(N0): for l in range(self._d * s + 1): frobij[l, s] = self._p * Dj[s] * self._frobpow_list[s][l] return frobij
def _N0_RH(): return ceil( log(2 * binomial(2 * self._genus, self._genus), self._p) + self._genus * self._n / ZZ(2))
def frobenius_polynomial(self): r""" Return the characteristic polynomial of Frobenius. EXAMPLES: Hyperelliptic curves:: sage: p = 11 sage: x = PolynomialRing(GF(p),"x").gen() sage: f = x^7 + 4*x^2 + 10*x + 4 sage: CyclicCover(2, f).frobenius_polynomial() == \ ....: HyperellipticCurve(f).frobenius_polynomial() True sage: f = 2*x^5 + 4*x^3 + x^2 + 2*x + 1 sage: CyclicCover(2, f).frobenius_polynomial() == \ ....: HyperellipticCurve(f).frobenius_polynomial() True sage: f = 2*x^6 + 4*x^4 + x^3 + 2*x^2 + x sage: CyclicCover(2, f).frobenius_polynomial() == \ ....: HyperellipticCurve(f).frobenius_polynomial() True sage: p = 1117 sage: x = PolynomialRing(GF(p),"x").gen() sage: f = x^9 + 4*x^2 + 10*x + 4 sage: CyclicCover(2, f).frobenius_polynomial() == \ ....: HyperellipticCurve(f).frobenius_polynomial() # long time True sage: f = 2*x^5 + 4*x^3 + x^2 + 2*x + 1 sage: CyclicCover(2, f).frobenius_polynomial() == \ ....: HyperellipticCurve(f).frobenius_polynomial() True Superelliptic curves:: sage: p = 11 sage: x = PolynomialRing(GF(p),"x").gen() sage: CyclicCover(3, x^4 + 4*x^3 + 9*x^2 + 3*x + 1).frobenius_polynomial() x^6 + 21*x^4 + 231*x^2 + 1331 sage: CyclicCover(4, x^3 + x + 1).frobenius_polynomial() x^6 + 2*x^5 + 11*x^4 + 121*x^2 + 242*x + 1331 sage: p = 4999 sage: x = PolynomialRing(GF(p),"x").gen() sage: CyclicCover(4, x^3 - 1).frobenius_polynomial() == \ ....: CyclicCover(3, x^4 + 1).frobenius_polynomial() True sage: CyclicCover(3, x^4 + 4*x^3 + 9*x^2 + 3*x + 1).frobenius_polynomial() x^6 + 180*x^5 + 20988*x^4 + 1854349*x^3 + 104919012*x^2 + 4498200180*x + 124925014999 sage: CyclicCover(4,x^5 + x + 1).frobenius_polynomial() x^12 - 64*x^11 + 5018*x^10 - 488640*x^9 + 28119583*x^8 - 641791616*x^7 + 124245485932*x^6 - 3208316288384*x^5 + 702708407289583*x^4 - 61043359329111360*x^3 + 3133741752599645018*x^2 - 199800079984001599936*x + 15606259372500374970001 sage: CyclicCover(11, PolynomialRing(GF(1129), 'x')([-1] + [0]*(5-1) + [1])).frobenius_polynomial() # long time x^40 + 7337188909826596*x^30 + 20187877911930897108199045855206*x^20 + 24687045654725446027864774006541463602997309796*x^10 + 11320844849639649951608809973589776933203136765026963553258401 sage: CyclicCover(3, PolynomialRing(GF(1009^2), 'x')([-1] + [0]*(5-1) + [1])).frobenius_polynomial() # long time x^8 + 532*x^7 - 2877542*x^6 - 242628176*x^5 + 4390163797795*x^4 - 247015136050256*x^3 - 2982540407204025062*x^2 + 561382189105547134612*x + 1074309286591662654798721 A non-monic example checking that :trac:`29015` is fixed:: sage: a = 3 sage: K.<s>=GF(83^3); sage: R.<x>= PolynomialRing(K) sage: h = s*x^4 +x*3+ 8; sage: C = CyclicCover(a,h) sage: C.frobenius_polynomial() x^6 + 1563486*x^4 + 893980969482*x^2 + 186940255267540403 Non-superelliptic curves:: sage: p = 13 sage: x = PolynomialRing(GF(p),"x").gen() sage: C = CyclicCover(4, x^4 + 1) sage: C.frobenius_polynomial() x^6 - 6*x^5 + 3*x^4 + 60*x^3 + 39*x^2 - 1014*x + 2197 sage: R.<t> = PowerSeriesRing(Integers()) sage: C.projective_closure().zeta_series(2,t) 1 + 8*t + 102*t^2 + O(t^3) sage: C.frobenius_polynomial().reverse()(t)/((1-t)*(1-p*t)) + O(t^5) 1 + 8*t + 102*t^2 + 1384*t^3 + 18089*t^4 + O(t^5) sage: x = PolynomialRing(GF(11),"x").gen() sage: CyclicCover(4, x^6 - 11*x^3 + 70*x^2 - x + 961).frobenius_polynomial() # long time x^14 + 14*x^12 + 287*x^10 + 3025*x^8 + 33275*x^6 + 381997*x^4 + 2254714*x^2 + 19487171 sage: x = PolynomialRing(GF(4999),"x").gen() sage: CyclicCover(4, x^6 - 11*x^3 + 70*x^2 - x + 961).frobenius_polynomial() # long time x^14 - 4*x^13 - 2822*x^12 - 30032*x^11 + 37164411*x^10 - 152369520*x^9 + 54217349361*x^8 - 1021791160888*x^7 + 271032529455639*x^6 - 3807714457169520*x^5 + 4642764601604000589*x^4 - 18754988504199390032*x^3 - 8809934776794570547178*x^2 - 62425037490001499880004*x + 78015690603129374475034999 sage: p = 11 sage: x = PolynomialRing(GF(p),"x").gen() sage: CyclicCover(3, 5*x^3 - 5*x + 13).frobenius_polynomial() x^2 + 11 sage: CyclicCover(3, x^6 + x^4 - x^3 + 2*x^2 - x - 1).frobenius_polynomial() x^8 + 32*x^6 + 462*x^4 + 3872*x^2 + 14641 sage: p = 4999 sage: x = PolynomialRing(GF(p),"x").gen() sage: CyclicCover(3, 5*x^3 - 5*x + 13).frobenius_polynomial() x^2 - 47*x + 4999 sage: CyclicCover(3, x^6 + x^4 - x^3 + 2*x^2 - x - 1).frobenius_polynomial() x^8 + 122*x^7 + 4594*x^6 - 639110*x^5 - 82959649*x^4 - 3194910890*x^3 + 114804064594*x^2 + 15240851829878*x + 624500149980001 sage: p = 11 sage: x = PolynomialRing(GF(p),"x").gen() sage: CyclicCover(5, x^5 + x).frobenius_polynomial() # long time x^12 + 4*x^11 + 22*x^10 + 108*x^9 + 503*x^8 + 1848*x^7 + 5588*x^6 + 20328*x^5 + 60863*x^4 + 143748*x^3 + 322102*x^2 + 644204*x + 1771561 sage: CyclicCover(5, 2*x^5 + x).frobenius_polynomial() # long time x^12 - 9*x^11 + 42*x^10 - 108*x^9 - 47*x^8 + 1782*x^7 - 8327*x^6 + 19602*x^5 - 5687*x^4 - 143748*x^3 + 614922*x^2 - 1449459*x + 1771561 sage: p = 49999 sage: x = PolynomialRing(GF(p),"x").gen() sage: CyclicCover(5, x^5 + x ).frobenius_polynomial() # long time x^12 + 299994*x^10 + 37498500015*x^8 + 2499850002999980*x^6 + 93742500224997000015*x^4 + 1874812507499850001499994*x^2 + 15623125093747500037499700001 sage: CyclicCover(5, 2*x^5 + x).frobenius_polynomial() # long time x^12 + 299994*x^10 + 37498500015*x^8 + 2499850002999980*x^6 + 93742500224997000015*x^4 + 1874812507499850001499994*x^2 + 15623125093747500037499700001 TESTS:: sage: for _ in range(5): # long time ....: fail = False ....: p = random_prime(500, lbound=5) ....: for i in range(1, 4): ....: F = GF(p**i) ....: Fx = PolynomialRing(F, 'x') ....: b = F.random_element() ....: while b == 0: ....: b = F.random_element() ....: E = EllipticCurve(F, [0, b]) ....: C1 = CyclicCover(3, Fx([-b, 0, 1])) ....: C2 = CyclicCover(2, Fx([b, 0, 0, 1])) ....: frob = [elt.frobenius_polynomial() for elt in [E, C1, C2]] ....: if len(set(frob)) != 1: ....: E ....: C1 ....: C2 ....: frob ....: fail = True ....: break ....: if fail: ....: break ....: else: ....: True True """ self._init_frob() F = self.frobenius_matrix(self._N0) def _denominator(): R = PolynomialRing(ZZ, "T") T = R.gen() denom = R(1) lc = self._f.list()[-1] if lc == 1: # MONIC for i in range(2, self._delta + 1): if self._delta % i == 0: phi = euler_phi(i) G = IntegerModRing(i) ki = G(self._q).multiplicative_order() denom = denom * (T**ki - 1)**(phi // ki) return denom else: # Non-monic x = PolynomialRing(self._Fq, "x").gen() f = x**self._delta - lc L = f.splitting_field("a") roots = [r for r, _ in f.change_ring(L).roots()] roots_dict = dict([(r, i) for i, r in enumerate(roots)]) rootsfrob = [ L.frobenius_endomorphism(self._Fq.degree())(r) for r in roots ] m = zero_matrix(len(roots)) for i, r in enumerate(roots): m[i, roots_dict[rootsfrob[i]]] = 1 return R(R(m.characteristic_polynomial()) // (T - 1)) denom = _denominator() R = PolynomialRing(ZZ, "x") if self._nodenominators: min_val = 0 else: # are there any denominators in F? min_val = min( self._Qq(elt).valuation() for row in F.rows() for elt in row) if min_val >= 0: prec = _N0_nodenominators(self._p, self._genus, self._n) charpoly_prec = [ prec + i for i in reversed(range(1, self._genus + 1)) ] + [prec] * (self._genus + 1) cp = charpoly_frobenius(F, charpoly_prec, self._p, 1, self._n, denom.list()) return R(cp) else: cp = F.charpoly().reverse() denom = denom.reverse() PS = PowerSeriesRing(self._Zp, "T") cp = PS(cp) / PS(denom) cp = cp.padded_list(self._genus + 1) cpZZ = [None for _ in range(2 * self._genus + 1)] cpZZ[0] = 1 cpZZ[-1] = self._p**self._genus for i in range(1, self._genus + 1): cmod = cp[i] bound = binomial(2 * self._genus, i) * self._p**(i * self._n * 0.5) localmod = self._p**(ceil(log(bound, self._p))) c = cmod.lift() % localmod if c > bound: c = -(-cmod.lift() % localmod) cpZZ[i] = c if i != self._genus + 1: cpZZ[2 * self._genus - i] = c * self._p**(self._genus - i) cpZZ.reverse() return R(cpZZ)
def BinomialRandomUniform(self, n, k, p): r""" Return a random `k`-uniform hypergraph on `n` points, in which each edge is inserted independently with probability `p`. - ``n`` -- number of nodes of the graph - ``k`` -- uniformity - ``p`` -- probability of an edge EXAMPLES:: sage: hypergraphs.BinomialRandomUniform(50, 3, 1).num_blocks() 19600 sage: hypergraphs.BinomialRandomUniform(50, 3, 0).num_blocks() 0 TESTS:: sage: hypergraphs.BinomialRandomUniform(50, 3, -0.1) Traceback (most recent call last): ... ValueError: edge probability should be in [0,1] sage: hypergraphs.BinomialRandomUniform(50, 3, 1.1) Traceback (most recent call last): ... ValueError: edge probability should be in [0,1] sage: hypergraphs.BinomialRandomUniform(-50, 3, 0.17) Traceback (most recent call last): ... ValueError: number of vertices should be non-negative sage: hypergraphs.BinomialRandomUniform(50.9, 3, 0.17) Traceback (most recent call last): ... ValueError: number of vertices should be an integer sage: hypergraphs.BinomialRandomUniform(50, -3, 0.17) Traceback (most recent call last): ... ValueError: the uniformity should be non-negative sage: hypergraphs.BinomialRandomUniform(50, I, 0.17) Traceback (most recent call last): ... ValueError: the uniformity should be an integer """ from sage.rings.integer import Integer if n < 0: raise ValueError("number of vertices should be non-negative") try: nverts = Integer(n) except TypeError: raise ValueError("number of vertices should be an integer") if k < 0: raise ValueError("the uniformity should be non-negative") try: uniformity = Integer(k) except TypeError: raise ValueError("the uniformity should be an integer") if not 0 <= p <= 1: raise ValueError("edge probability should be in [0,1]") import numpy.random as nrn from sage.functions.other import binomial m = nrn.binomial(binomial(nverts, uniformity), p) return hypergraphs.UniformRandomUniform(n, k, m)
def principal_specialization(self, n=infinity, q=None): r""" Return the principal specialization of a symmetric function. The *principal specialization* of order `n` at `q` is the ring homomorphism `ps_{n,q}` from the ring of symmetric functions to another commutative ring `R` given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}` and `x_i \mapsto 0` for `i > n`. Here, `q` is a given element of `R`, and we assume that the variables of our symmetric functions are `x_1, x_2, x_3, \ldots`. (To be more precise, `ps_{n,q}` is a `K`-algebra homomorphism, where `K` is the base ring.) See Section 7.8 of [EnumComb2]_. The *stable principal specialization* at `q` is the ring homomorphism `ps_q` from the ring of symmetric functions to another commutative ring `R` given by `x_i \mapsto q^{i-1}` for all `i`. This is well-defined only if the resulting infinite sums converge; thus, in particular, setting `q = 1` in the stable principal specialization is an invalid operation. INPUT: - ``n`` (default: ``infinity``) -- a nonnegative integer or ``infinity``, specifying whether to compute the principal specialization of order ``n`` or the stable principal specialization. - ``q`` (default: ``None``) -- the value to use for `q`; the default is to create a ring of polynomials in ``q`` (or a field of rational functions in ``q``) over the given coefficient ring. We use the formulas from Proposition 7.8.3 of [EnumComb2]_ (using Gaussian binomial coefficients `\binom{u}{v}_q`): .. MATH:: ps_{n,q}(h_\lambda) = \prod_i \binom{n+\lambda_i-1}{\lambda_i}_q, ps_{n,1}(h_\lambda) = \prod_i \binom{n+\lambda_i-1}{\lambda_i}, ps_q(h_\lambda) = 1 / \prod_i \prod_{j=1}^{\lambda_i} (1-q^j). EXAMPLES:: sage: h = SymmetricFunctions(QQ).h() sage: x = h[2,1] sage: x.principal_specialization(3) q^6 + 2*q^5 + 4*q^4 + 4*q^3 + 4*q^2 + 2*q + 1 sage: x = 3*h[2] + 2*h[1] + 1 sage: x.principal_specialization(3, q=var("q")) 2*(q^3 - 1)/(q - 1) + 3*(q^4 - 1)*(q^3 - 1)/((q^2 - 1)*(q - 1)) + 1 TESTS:: sage: x = h.zero() sage: s = x.principal_specialization(3); s 0 """ from sage.combinat.q_analogues import q_binomial def get_variable(ring, name): try: ring(name) except TypeError: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing return PolynomialRing(ring, name).gen() else: raise ValueError( "the variable %s is in the base ring, pass it explicitly" % name) if q is None: q = get_variable(self.base_ring(), 'q') if q == 1: if n == infinity: raise ValueError( "the stable principal specialization at q=1 is not defined" ) f = lambda partition: prod( binomial(n + part - 1, part) for part in partition) elif n == infinity: f = lambda partition: prod(1 / prod( (1 - q**i) for i in range(1, part + 1)) for part in partition) else: f = lambda partition: prod( q_binomial(n + part - 1, part, q=q) for part in partition) return self.parent()._apply_module_morphism(self, f, q.parent())
def _standardize_s_coeff(s_coeff, index_set, ce, parity=None): """ Convert an input dictionary to structure constants of this Lie conformal algebra. INPUT: - ``s_coeff`` -- a dictionary as in :class:`LieConformalAlgebraWithStructureCoefficients<sage.\ algebras.lie_conformal_algebras.lie_conformal_algebra_with_\ structure_coefficients.LieConformalAlgebraWithStructure\ Coefficients>`. - ``index_set` -- A finite enumerated set indexing the generators (not counting the central elements). - ``ce`` -- a tuple of ``str``; a list of names for the central generators of this Lie conformal algebra - ``parity`` -- a tuple of `0` or `1` (Default: tuple of `0`); this tuple specifies the parity of each non-central generator. OUTPUT: A finite Family representing ``s_coeff`` in the input. It contains superfluous information that can be obtained by skew-symmetry but that improves speed in computing OPE for vertex algebras. EXAMPLES:: sage: virdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2},3:{('C', 0):1/2}}} sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir._standardize_s_coeff(virdict, Family(('L',)), ('C',)) Finite family {('L', 'L'): ((0, ((('L', 1), 1),)), (1, ((('L', 0), 2),)), (3, ((('C', 0), 1/2),)))} """ if parity is None: parity = (0, ) * index_set.cardinality() index_to_parity = {i: p for (i, p) in zip(index_set, parity)} sc = {} #mypair has a pair of generators for mypair in s_coeff.keys(): #e.g. v = { 0: { (L,2):3, (G,3):1}, 1:{(L,1),2} } v = s_coeff[mypair] key = tuple(mypair) vals = {} for l in v.keys(): lth_product = {k: y for k, y in v[l].items() if y} if lth_product: vals[l] = lth_product myvals = tuple([(k, tuple(v.items())) for k, v in vals.items() if v]) if key in sc.keys() and sorted(sc[key]) != sorted(myvals): raise ValueError("two distinct values given for one "\ "and the same bracket, skew-symmetry"\ "is not satisfied?") if myvals: sc[key] = myvals #We now add the skew-symmetric part to optimize #brackets computations later key = (mypair[1], mypair[0]) if index_to_parity[mypair[0]] * index_to_parity[mypair[1]]: parsgn = -1 else: parsgn = 1 maxpole = max(v.keys()) vals = {} for k in range(maxpole + 1): kth_product = {} for j in range(maxpole + 1 - k): if k + j in v.keys(): for i in v[k + j]: if (i[0] not in ce) or (i[0] in ce and i[1] + j == 0): kth_product[(i[0],i[1]+j)] = \ kth_product.get((i[0], i[1]+j), 0) kth_product[(i[0],i[1]+j)] += parsgn*\ v[k+j][i]*(-1)**(k+j+1)*binomial(i[1]+j,j) kth_product = {k: v for k, v in kth_product.items() if v} if kth_product: vals[k] = kth_product myvals = tuple([(k, tuple(v.items())) for k, v in vals.items() if v]) if key in sc.keys() and sorted(sc[key]) != sorted(myvals): raise ValueError("two distinct values given for one "\ "and the same bracket. "\ "Skew-symmetry is not satisfied?") if myvals: sc[key] = myvals return Family(sc)
def principal_specialization(self, n=infinity, q=None): r""" Return the principal specialization of a symmetric function. The *principal specialization* of order `n` at `q` is the ring homomorphism `ps_{n,q}` from the ring of symmetric functions to another commutative ring `R` given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}` and `x_i \mapsto 0` for `i > n`. Here, `q` is a given element of `R`, and we assume that the variables of our symmetric functions are `x_1, x_2, x_3, \ldots`. (To be more precise, `ps_{n,q}` is a `K`-algebra homomorphism, where `K` is the base ring.) See Section 7.8 of [EnumComb2]_. The *stable principal specialization* at `q` is the ring homomorphism `ps_q` from the ring of symmetric functions to another commutative ring `R` given by `x_i \mapsto q^{i-1}` for all `i`. This is well-defined only if the resulting infinite sums converge; thus, in particular, setting `q = 1` in the stable principal specialization is an invalid operation. INPUT: - ``n`` (default: ``infinity``) -- a nonnegative integer or ``infinity``, specifying whether to compute the principal specialization of order ``n`` or the stable principal specialization. - ``q`` (default: ``None``) -- the value to use for `q`; the default is to create a ring of polynomials in ``q`` (or a field of rational functions in ``q``) over the given coefficient ring. For ``q=1`` and finite ``n`` we use the formula from Proposition 7.8.3 of [EnumComb2]_: .. MATH:: ps_{n,1}(m_\lambda) = \binom{n}{\ell(\lambda)} \binom{\ell(\lambda)}{m_1(\lambda), m_2(\lambda),\dots}, where `\ell(\lambda)` denotes the length of `\lambda`. In all other cases, we convert to complete homogeneous symmetric functions. EXAMPLES:: sage: m = SymmetricFunctions(QQ).m() sage: x = m[3,1] sage: x.principal_specialization(3) q^7 + q^6 + q^5 + q^3 + q^2 + q sage: x = 5*m[2] + 3*m[1] + 1 sage: x.principal_specialization(3, q=var("q")) -10*(q^3 - 1)*q/(q - 1) + 5*(q^3 - 1)^2/(q - 1)^2 + 3*(q^3 - 1)/(q - 1) + 1 TESTS:: sage: m.zero().principal_specialization(3) 0 """ if q == 1: if n == infinity: raise ValueError( "the stable principal specialization at q=1 is not defined" ) f = lambda partition: binomial(n, len( partition)) * multinomial(partition.to_exp()) return self.parent()._apply_module_morphism( self, f, q.parent()) # heuristically, it seems fastest to fall back to the # elementary basis - using the powersum basis would # introduce singularities, because it is not a Z-basis return self.parent().realization_of().elementary()( self).principal_specialization(n=n, q=q)
def fiej(i, j, d): # fiejcoeff_on_highest_weight """ INPUT: - `i`, `j`, `d` -- nonnegative integers OUTPUT: a nonnegative integer Let $f = x\partial_y and e = y\partial_x$, and `p` be a highest weight polynomial of weight `d`. Then `c=fiej(i,j,d)` is such that `f^i e^j p = c e^{j-i} p`. `c` is given by the formula:: .. MATH:: \prod_{k = j-i+1}^j (kd - 2 \binom{k}{2}) EXAMPLES:: sage: R = QQ['x,y'] sage: R.inject_variables() Defining x, y sage: def f(p): return x*diff(p,y) sage: def e(p): return y*diff(p,x) sage: fiej(0,0,3) 1 sage: fiej(0,1,3) 1 sage: f(e(x^3)) / x^3 3 sage: fiej(1,1,3) 3 sage: f(f(e(x^3))) 0 sage: fiej(2,1,3) 0 sage: fiej(0,2,3) 1 sage: f(e(e(x^3))) / e(x^3) 4 sage: fiej(1,2,3) 4 sage: f(f(e(e(x^3)))) / x^3 12 sage: fiej(2,2,3) 12 sage: fiej(0,3,3) 1 sage: f(e(e(e(x^3)))) / e(e(x^3)) 3 sage: fiej(1,3,3) 3 sage: f(f(e(e(e(x^3))))) / e(x^3) 12 sage: f(f(f(e(e(e(x^3)))))) / x^3 36 sage: fiej(3,3,3) 36 sage: fiej(4,3,3) 0 sage: f(f(f(e(e(e(x^9)))))) / x^9 3024 sage: fiej(3,3,9) 3024 """ return binomial(j, i) * binomial(d - j + i, i) * factorial(i)**2
def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, precision=None, normalized=True): r""" Return the asymptotic expansion of the coefficients of an power series with specified pole and logarithmic singularity. More precisely, this extracts the `n`-th coefficient .. MATH:: [z^n] \left(\frac{1}{1-z/\zeta}\right)^\alpha \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)^\beta \left(\frac{1}{z/\zeta} \log \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)\right)^\delta (if ``normalized=True``, the default) or .. MATH:: [z^n] \left(\frac{1}{1-z/\zeta}\right)^\alpha \left(\log \frac{1}{1-z/\zeta}\right)^\beta \left(\log \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)\right)^\delta (if ``normalized=False``). INPUT: - ``var`` -- a string for the variable name. - ``zeta`` -- (default: `1`) the location of the singularity. - ``alpha`` -- (default: `0`) the pole order of the singularty. - ``beta`` -- (default: `0`) the order of the logarithmic singularity. - ``delta`` -- (default: `0`) the order of the log-log singularity. Not yet implemented for ``delta != 0``. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``normalized`` -- (default: ``True``) a boolean, see above. OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.SingularityAnalysis('n', alpha=1) 1 sage: asymptotic_expansions.SingularityAnalysis('n', alpha=2) n + 1 sage: asymptotic_expansions.SingularityAnalysis('n', alpha=3) 1/2*n^2 + 3/2*n + 1 sage: _.parent() Asymptotic Ring <n^ZZ> over Rational Field :: sage: asymptotic_expansions.SingularityAnalysis('n', alpha=-3/2, ....: precision=3) 3/4/sqrt(pi)*n^(-5/2) + 45/32/sqrt(pi)*n^(-7/2) + 1155/512/sqrt(pi)*n^(-9/2) + O(n^(-11/2)) sage: asymptotic_expansions.SingularityAnalysis('n', alpha=-1/2, ....: precision=3) -1/2/sqrt(pi)*n^(-3/2) - 3/16/sqrt(pi)*n^(-5/2) - 25/256/sqrt(pi)*n^(-7/2) + O(n^(-9/2)) sage: asymptotic_expansions.SingularityAnalysis('n', alpha=1/2, ....: precision=4) 1/sqrt(pi)*n^(-1/2) - 1/8/sqrt(pi)*n^(-3/2) + 1/128/sqrt(pi)*n^(-5/2) + 5/1024/sqrt(pi)*n^(-7/2) + O(n^(-9/2)) sage: _.parent() Asymptotic Ring <n^QQ> over Symbolic Constants Subring :: sage: S = SR.subring(rejecting_variables=('n',)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=S.var('a'), ....: precision=4).map_coefficients(lambda c: c.factor()) 1/gamma(a)*n^(a - 1) + (1/2*(a - 1)*a/gamma(a))*n^(a - 2) + (1/24*(3*a - 1)*(a - 1)*(a - 2)*a/gamma(a))*n^(a - 3) + (1/48*(a - 1)^2*(a - 2)*(a - 3)*a^2/gamma(a))*n^(a - 4) + O(n^(a - 5)) sage: _.parent() Asymptotic Ring <n^(Symbolic Subring rejecting the variable n)> over Symbolic Subring rejecting the variable n :: sage: ae = asymptotic_expansions.SingularityAnalysis('n', ....: alpha=1/2, beta=1, precision=4); ae 1/sqrt(pi)*n^(-1/2)*log(n) + ((euler_gamma + 2*log(2))/sqrt(pi))*n^(-1/2) - 5/8/sqrt(pi)*n^(-3/2)*log(n) + (1/8*(3*euler_gamma + 6*log(2) - 8)/sqrt(pi) - (euler_gamma + 2*log(2) - 2)/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) sage: n = ae.parent().gen() sage: ae.subs(n=n-1).map_coefficients(lambda x: x.canonicalize_radical()) 1/sqrt(pi)*n^(-1/2)*log(n) + ((euler_gamma + 2*log(2))/sqrt(pi))*n^(-1/2) - 1/8/sqrt(pi)*n^(-3/2)*log(n) + (-1/8*(euler_gamma + 2*log(2))/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) :: sage: asymptotic_expansions.SingularityAnalysis('n', ....: alpha=1, beta=1/2, precision=4) log(n)^(1/2) + 1/2*euler_gamma*log(n)^(-1/2) + (-1/8*euler_gamma^2 + 1/48*pi^2)*log(n)^(-3/2) + (1/16*euler_gamma^3 - 1/32*euler_gamma*pi^2 + 1/8*zeta(3))*log(n)^(-5/2) + O(log(n)^(-7/2)) :: sage: ae = asymptotic_expansions.SingularityAnalysis('n', ....: alpha=0, beta=2, precision=14) sage: n = ae.parent().gen() sage: ae.subs(n=n-2) 2*n^(-1)*log(n) + 2*euler_gamma*n^(-1) - n^(-2) - 1/6*n^(-3) + O(n^(-5)) :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=-1/2, beta=1, precision=2, normalized=False) -1/2/sqrt(pi)*n^(-3/2)*log(n) + (-1/2*(euler_gamma + 2*log(2) - 2)/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1/2, alpha=0, beta=1, precision=3, normalized=False) 2^n*n^(-1) + O(2^n*n^(-2)) ALGORITHM: See [FS2009]_ together with the `errata list <http://algo.inria.fr/flajolet/Publications/AnaCombi/errata.pdf>`_. REFERENCES: .. [FS2009] Philippe Flajolet and Robert Sedgewick, `Analytic combinatorics <http://algo.inria.fr/flajolet/Publications/AnaCombi/book.pdf>`_. Cambridge University Press, Cambridge, 2009. TESTS:: sage: ex = asymptotic_expansions.SingularityAnalysis('n', alpha=-1/2, ....: precision=4) sage: n = ex.parent().gen() sage: coefficients = ((1-x)^(1/2)).series( ....: x, 21).truncate().coefficients(x, sparse=False) sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda k: coefficients[k], [5, 10, 20]) [(5, 0.015778873294?), (10, 0.01498952777?), (20, 0.0146264622?)] sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=2) 1/2*n^2 + 3/2*n + O(1) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=3) 1/2*n^2 + 3/2*n + 1 sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=4) 1/2*n^2 + 3/2*n + 1 :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=0) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=-1) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'm', alpha=-1/2, precision=3) -1/2/sqrt(pi)*m^(-3/2) - 3/16/sqrt(pi)*m^(-5/2) - 25/256/sqrt(pi)*m^(-7/2) + O(m^(-9/2)) sage: _.parent() Asymptotic Ring <m^QQ> over Symbolic Constants Subring Location of the singularity:: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=2, precision=3) (1/2)^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=1/2, precision=3) 2^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=CyclotomicField(3).gen(), ....: precision=3) (-zeta3 - 1)^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=4, zeta=2, precision=3) 1/6*(1/2)^n*n^3 + (1/2)^n*n^2 + 11/6*(1/2)^n*n + O((1/2)^n) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=-1, zeta=2, precision=3) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1/2, zeta=2, precision=3) 1/sqrt(pi)*(1/2)^n*n^(-1/2) - 1/8/sqrt(pi)*(1/2)^n*n^(-3/2) + 1/128/sqrt(pi)*(1/2)^n*n^(-5/2) + O((1/2)^n*n^(-7/2)) The following tests correspond to Table VI.5 in [FS2009]_. :: sage: A.<n> = AsymptoticRing('n^QQ * log(n)^QQ', QQ) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=-1/2, beta=1, precision=2, ....: normalized=False) * (- sqrt(pi*n^3)) 1/2*log(n) + 1/2*euler_gamma + log(2) - 1 + O(n^(-1)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=0, beta=1, precision=3, ....: normalized=False) n^(-1) + O(n^(-2)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=0, beta=2, precision=14, ....: normalized=False) * n 2*log(n) + 2*euler_gamma - n^(-1) - 1/6*n^(-2) + O(n^(-4)) sage: (asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1/2, beta=1, precision=4, ....: normalized=False) * sqrt(pi*n)).\ ....: map_coefficients(lambda x: x.expand()) log(n) + euler_gamma + 2*log(2) - 1/8*n^(-1)*log(n) + (-1/8*euler_gamma - 1/4*log(2))*n^(-1) + O(n^(-2)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1, beta=1, precision=13, ....: normalized=False) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1, beta=2, precision=4, ....: normalized=False) log(n)^2 + 2*euler_gamma*log(n) + euler_gamma^2 - 1/6*pi^2 + O(n^(-1)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=3/2, beta=1, precision=3, ....: normalized=False) * sqrt(pi/n) 2*log(n) + 2*euler_gamma + 4*log(2) - 4 + 3/4*n^(-1)*log(n) + O(n^(-1)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=2, beta=1, precision=5, ....: normalized=False) n*log(n) + (euler_gamma - 1)*n + log(n) + euler_gamma + 1/2 + O(n^(-1)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=2, beta=2, precision=4, ....: normalized=False) / n log(n)^2 + (2*euler_gamma - 2)*log(n) - 2*euler_gamma + euler_gamma^2 - 1/6*pi^2 + 2 + n^(-1)*log(n)^2 + O(n^(-1)*log(n)) Be aware that the last result does *not* coincide with [FS2009]_, they do have a different error term. Checking parameters:: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, 1, 1/2, precision=0, normalized=False) Traceback (most recent call last): ... ValueError: beta and delta must be integers sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, 1, 1, 1/2, normalized=False) Traceback (most recent call last): ... ValueError: beta and delta must be integers :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=0, beta=0, delta=1, precision=3) Traceback (most recent call last): ... NotImplementedError: not implemented for delta!=0 """ from itertools import islice, count from asymptotic_ring import AsymptoticRing from growth_group import ExponentialGrowthGroup, \ MonomialGrowthGroup from sage.arith.all import falling_factorial from sage.categories.cartesian_product import cartesian_product from sage.functions.other import binomial, gamma from sage.calculus.calculus import limit from sage.misc.cachefunc import cached_function from sage.arith.srange import srange from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.symbolic.ring import SR SCR = SR.subring(no_variables=True) s = SR('s') iga = 1/gamma(alpha) if iga.parent() is SR: try: iga = SCR(iga) except TypeError: pass coefficient_ring = iga.parent() if beta != 0: coefficient_ring = SCR @cached_function def inverse_gamma_derivative(shift, r): """ Return value of `r`-th derivative of 1/Gamma at alpha-shift. """ if r == 0: result = iga*falling_factorial(alpha-1, shift) else: result = limit((1/gamma(s)).diff(s, r), s=alpha-shift) try: return coefficient_ring(result) except TypeError: return result if isinstance(alpha, int): alpha = ZZ(alpha) if isinstance(beta, int): beta = ZZ(beta) if isinstance(delta, int): delta = ZZ(delta) if precision is None: precision = AsymptoticRing.__default_prec__ if not normalized and not (beta in ZZ and delta in ZZ): raise ValueError("beta and delta must be integers") if delta != 0: raise NotImplementedError("not implemented for delta!=0") groups = [] if zeta != 1: groups.append(ExponentialGrowthGroup((1/zeta).parent(), var)) groups.append(MonomialGrowthGroup(alpha.parent(), var)) if beta != 0: groups.append(MonomialGrowthGroup(beta.parent(), 'log({})'.format(var))) group = cartesian_product(groups) A = AsymptoticRing(growth_group=group, coefficient_ring=coefficient_ring, default_prec=precision) n = A.gen() if zeta == 1: exponential_factor = 1 else: exponential_factor = n.rpow(1/zeta) if beta in ZZ and beta >= 0: it = ((k, r) for k in count() for r in srange(beta+1)) k_max = precision else: it = ((0, r) for r in count()) k_max = 0 if beta != 0: log_n = n.log() else: # avoid construction of log(n) # because it does not exist in growth group. log_n = 1 it = reversed(list(islice(it, precision+1))) if normalized: beta_denominator = beta else: beta_denominator = 0 L = _sa_coefficients_lambda_(max(1, k_max), beta=beta_denominator) (k, r) = next(it) result = (n**(-k) * log_n**(-r)).O() if alpha in ZZ and beta == 0: if alpha > 0 and alpha <= precision: result = A(0) elif alpha <= 0 and precision > 0: from misc import NotImplementedOZero raise NotImplementedOZero(A) for (k, r) in it: result += binomial(beta, r) * \ sum(L[(k, ell)] * (-1)**ell * inverse_gamma_derivative(ell, r) for ell in srange(k, 2*k+1) if (k, ell) in L) * \ n**(-k) * log_n**(-r) result *= exponential_factor * n**(alpha-1) * log_n**beta return result