def load_hashes(): hashfile = opj("DATA", "hash", "thash.out") clusters = defaultdict(list) seen = defaultdict(set) with open(hashfile) as F: for line in F: n, t, N, hsh = map(ZZ, line.strip().split()) seen[n].add(t) if N == 1024 or N > 2000 and not libgap.SmallGroupsAvailable(N): clusters[N, hsh].append(f"{n}T{t}") # We manually add some groups where we didn't succeed in computing the hash, but know that they are unique up to isomorphism (up to degree 47 at least) for n in range(39, 48): tmax = ZZ(libgap.NrTransitiveGroups(n)) clusters[factorial(n), -1] = [f"{n}T{tmax}"] # Sn clusters[factorial(n) // 2, -1] = [f"{n}T{tmax-1}"] # An seen[n].update(set([tmax - 1, tmax])) for n in range(1, 47): if n not in seen: print("Missing degree", n) else: cnt = libgap.NrTransitiveGroups(n) if len(seen[n]) != cnt: print(f"Missing {cnt - len(seen[n])} in degree {n}") if cnt - len(seen[n]) < 10: print([i for i in range(1, cnt + 1) if i not in seen[n]]) else: for i in range(1, cnt + 1): if i not in seen[n]: print("Smallest", i) break for i in range(cnt, 0, -1): if i not in seen[n]: print("Largest", i) break return clusters
def _bracket_(self, right): """ The lambda bracket of these two elements. The result is a dictionary with non-negative integer keys. The value corresponding to the entry `j` is ``self_{(j)}right``. EXAMPLES:: sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 sage: L.bracket(L) {0: TL, 1: 2*L, 3: 1/2*C} sage: L.T().bracket(L) {1: -TL, 2: -4*L, 4: -2*C} sage: R = lie_conformal_algebras.Affine(QQbar, 'A1', names=('e','h','f')); R The affine Lie conformal algebra of type ['A', 1] over Algebraic Field sage: R.inject_variables() Defining e, h, f, K sage: e.bracket(f) {0: h, 1: K} sage: h.bracket(h.T()) {2: 4*K} """ p = self.parent() if self.is_monomial() and right.is_monomial(): if self.is_zero() or right.is_zero(): return {} s_coeff = p._s_coeff a, k = self.index() coefa = self.monomial_coefficients()[(a, k)] b, m = right.index() coefb = right.monomial_coefficients()[(b, m)] try: mbr = dict(s_coeff[(a, b)]) except KeyError: return {} pole = max(mbr.keys()) ret = {l: coefa*coefb*(-1)**k/factorial(k)*sum(factorial(l)\ /factorial(m+k+j-l)/factorial(l-k-j)/factorial(j)*\ mbr[j].T(m+k+j-l) for j in mbr if j >= l-m-k and\ j <= l-k) for l in range(m+k+pole+1)} return {k: v for k, v in ret.items() if v} diclist = [i._bracket_(j) for i in self.terms() for j in right.terms()] ret = {} pz = p.zero() for d in diclist: for k in d: ret[k] = ret.get(k, pz) + d[k] return {k: v for k, v in ret.items() if v}
def is_symmetric(self): r""" Determine if a `NCSym^*` function, expressed in the `\mathbf{w}` basis, is symmetric. A function `f` in the `\mathbf{w}` basis is a symmetric function if it is in the image of `\chi^*`. That is to say we have .. MATH:: f = \sum_{\lambda} c_{\lambda} \prod_i m_i(\lambda)! \sum_{\lambda(A) = \lambda} \mathbf{w}_A where the second sum is over all set partitions `A` whose shape `\lambda(A)` is equal to `\lambda` and `m_i(\mu)` is the multiplicity of `i` in the partition `\mu`. OUTPUT: - ``True`` if `\lambda(A)=\lambda(B)` implies the coefficients of `\mathbf{w}_A` and `\mathbf{w}_B` are equal, ``False`` otherwise EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: elt = w.sum_of_partitions([2,1,1]) sage: elt.is_symmetric() True sage: elt -= 3*w.sum_of_partitions([1,1]) sage: elt.is_symmetric() True sage: w = SymmetricFunctionsNonCommutingVariables(ZZ).dual().w() sage: elt = w.sum_of_partitions([2,1,1]) / 2 sage: elt.is_symmetric() False sage: elt = w[[1,3],[2]] sage: elt.is_symmetric() False sage: elt = w[[1],[2,3]] + w[[1,2],[3]] + 2*w[[1,3],[2]] sage: elt.is_symmetric() False """ d = {} R = self.base_ring() for A, coeff in self: la = A.shape() exp = prod([factorial(_) for _ in la.to_exp()]) if la not in d: if coeff / exp not in R: return False d[la] = [coeff, 1] else: if d[la][0] != coeff: return False d[la][1] += 1 # Make sure we've seen each set partition of the shape return all( d[la][1] == SetPartitions(la.size(), la).cardinality() for la in d)
def f(partition): n = 0 for part in partition: if part != 1: return 0 n += 1 return t**n / factorial(n)
def f(partition): n = 0 m = 1 for part in partition: n += part m *= factorial(part) return t**n / m
def sum_of_partitions(self, la): r""" Return the sum over all sets partitions whose shape is ``la``, scaled by `\prod_i m_i!` where `m_i` is the multiplicity of `i` in ``la``. INPUT: - ``la`` -- an integer partition OUTPUT: - an element of ``self`` EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: w.sum_of_partitions([2,1,1]) 2*w{{1}, {2}, {3, 4}} + 2*w{{1}, {2, 3}, {4}} + 2*w{{1}, {2, 4}, {3}} + 2*w{{1, 2}, {3}, {4}} + 2*w{{1, 3}, {2}, {4}} + 2*w{{1, 4}, {2}, {3}} """ la = Partition(la) c = prod([factorial(_) for _ in la.to_exp()]) P = SetPartitions() return self.sum_of_terms([(P(m), c) for m in SetPartitions(sum(la), la)], distinct=True)
def is_symmetric(self): r""" Determine if a `NCSym^*` function, expressed in the `\mathbf{w}` basis, is symmetric. A function `f` in the `\mathbf{w}` basis is a symmetric function if it is in the image of `\chi^*`. That is to say we have .. MATH:: f = \sum_{\lambda} c_{\lambda} \prod_i m_i(\lambda)! \sum_{\lambda(A) = \lambda} \mathbf{w}_A where the second sum is over all set partitions `A` whose shape `\lambda(A)` is equal to `\lambda` and `m_i(\mu)` is the multiplicity of `i` in the partition `\mu`. OUTPUT: - ``True`` if `\lambda(A)=\lambda(B)` implies the coefficients of `\mathbf{w}_A` and `\mathbf{w}_B` are equal, ``False`` otherwise EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: elt = w.sum_of_partitions([2,1,1]) sage: elt.is_symmetric() True sage: elt -= 3*w.sum_of_partitions([1,1]) sage: elt.is_symmetric() True sage: w = SymmetricFunctionsNonCommutingVariables(ZZ).dual().w() sage: elt = w.sum_of_partitions([2,1,1]) / 2 sage: elt.is_symmetric() False sage: elt = w[[1,3],[2]] sage: elt.is_symmetric() False sage: elt = w[[1],[2,3]] + w[[1,2],[3]] + 2*w[[1,3],[2]] sage: elt.is_symmetric() False """ d = {} R = self.base_ring() for A, coeff in self: la = A.shape() exp = prod([factorial(_) for _ in la.to_exp()]) if la not in d: if coeff / exp not in R: return False d[la] = [coeff, 1] else: if d[la][0] != coeff: return False d[la][1] += 1 # Make sure we've seen each set partition of the shape return all(d[la][1] == SetPartitions(la.size(), la).cardinality() for la in d)
def logpp_binom(n, p, p_prec): """returns the (integral) power series p^n*(log_p(1+p*z)/log_p(1+p) choose n)""" #prod=1+0*z L = logpp_gam(p, p_prec) ans = prod([(L - j) for j in range(n)]) #for j in range(0,n): # prod=prod*(L-j) ans *= (p**n) / factorial(n) return ps_normalize(ans.truncate(p_prec), p, p_prec)
def logpp_binom(n, p, p_prec): """returns the (integral) power series p^n*(log_p(1+p*z)/log_p(1+p) choose n)""" #prod=1+0*z L = logpp_gam(p, p_prec) ans = prod([(L - j) for j in range(n)]) #for j in range(0,n): # prod=prod*(L-j) ans *= (p ** n) / factorial(n) return ps_normalize(ans.truncate(p_prec), p, p_prec)
def polynomial_derivative_on_basis(e, f): """ Return the differentiation of `f` by `e`. INPUT: - `e`, `f` -- exponent vectors representing two monomials `X^e` and `X^f` (type: :class:`sage.rings.polynomial.polydict.ETuple`) OUTPUT: - a pair `(g,c)` where `g` is an exponent vector and `c` a coefficient, representing the term `c X^g`, or :obj:`None` if the result is zero. Let `R=K[X]` be a multivariate polynomial ring. Write `X^e` for the monomial with exponent vector `e`, and `p(\partial)` the differential operator obtained by substituting each variable `x` in `p` by `\frac{\partial}{\partial x}`. This returns `X^e(\partial)(X^f)` EXAMPLES:: sage: from sage.rings.polynomial.polydict import ETuple sage: polynomial_derivative_on_basis(ETuple((4,0)), ETuple((4,0))) ((0, 0), 24) sage: polynomial_derivative_on_basis(ETuple((0,3)), ETuple((0,3))) ((0, 0), 6) sage: polynomial_derivative_on_basis(ETuple((0,1)), ETuple((0,3))) ((0, 2), 3) sage: polynomial_derivative_on_basis(ETuple((2,0)), ETuple((4,0))) ((2, 0), 12) sage: polynomial_derivative_on_basis(ETuple((2,1)), ETuple((4,3))) ((2, 2), 36) sage: polynomial_derivative_on_basis(ETuple((1,3)), ETuple((1,2))) sage: polynomial_derivative_on_basis(ETuple((2,0)), ETuple((1,2))) """ g = f.esub(e) if any(i < 0 for i in g): return None return (g, prod(factorial(i) / factorial(j) for (i, j) in zip(f, g)))
def flip(self): ##does not work correctly?? k = self.weight() try: k = ZZ(k) except TypeError: return NotImplemented u = factorial(-k) return WeilRepMockModularForm(k, self.gram_matrix(), (u * self.shadow()).fourier_expansion(), self.bol() / u, weilrep=self.weilrep())
def coeff(p, q): ret = QQ.one() last = 0 for val in p: count = 0 s = 0 while s != val: s += q[last+count] count += 1 ret /= factorial(count) last += count return ret
def pochhammer_numerator(alpha, p, m, verbose=False): rational = fractional_part(alpha + m / (1 - p)) R = GF(p) k = R(rational).lift() if k == 0: if verbose: print("issue alpha=%s m=%s" % (alpha, m)) return R(1) else: fact = R(factorial(k - 1)) res = (p - fact) if k % 2 else fact return res
def logp_binom(n, p, p_prec): """returns the (integral) power series (log_p(1+z)/log_p(1+p) choose n)""" #prod=1+0*z if n == 0: return PolynomialRing(QQ, 'y')(1) L = logp_gam(p, p_prec) ans = prod([(L - j) for j in range(n)]) #for j in range(0,n): # prod=prod*(L-j) ans = ans / factorial(n) return ps_normalize(ans.truncate(p_prec+1), p, p_prec) #Do we need the +1?
def coeff(p, q): ret = QQ.one() last = 0 for val in p: count = 0 s = 0 while s != val: s += q[last + count] count += 1 ret /= factorial(count) last += count return ret
def logp_binom(n, p, p_prec): """returns the (integral) power series (log_p(1+z)/log_p(1+p) choose n)""" #prod=1+0*z if n == 0: return PolynomialRing(QQ, 'y')(1) L = logp_gam(p, p_prec) ans = prod([(L - j) for j in range(n)]) #for j in range(0,n): # prod=prod*(L-j) ans = ans / factorial(n) return ps_normalize(ans.truncate(p_prec + 1), p, p_prec) #Do we need the +1?
def eval_formula(self, n, x): """ Evaluate ``chebyshev_T`` using an explicit formula. See [ASHandbook]_ 227 (p. 782) for details for the recurions. See also [EffCheby]_ for fast evaluation techniques. INPUT: - ``n`` -- an integer - ``x`` -- a value to evaluate the polynomial at (this can be any ring element) EXAMPLES:: sage: chebyshev_T.eval_formula(-1,x) x sage: chebyshev_T.eval_formula(0,x) 1 sage: chebyshev_T.eval_formula(1,x) x sage: chebyshev_T.eval_formula(2,0.1) == chebyshev_T._evalf_(2,0.1) True sage: chebyshev_T.eval_formula(10,x) 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 sage: chebyshev_T.eval_algebraic(10,x).expand() 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 """ if n < 0: return self.eval_formula(-n, x) elif n == 0: return parent(x).one() res = parent(x).zero() for j in xrange(0, n // 2 + 1): f = factorial(n - 1 - j) / factorial(j) / factorial(n - 2 * j) res += (-1) ** j * (2 * x) ** (n - 2 * j) * f res *= n / 2 return res
def eval_formula(self, n, x): """ Evaluate ``chebyshev_T`` using an explicit formula. See [ASHandbook]_ 227 (p. 782) for details for the recurions. See also [EffCheby]_ for fast evaluation techniques. INPUT: - ``n`` -- an integer - ``x`` -- a value to evaluate the polynomial at (this can be any ring element) EXAMPLES:: sage: chebyshev_T.eval_formula(-1,x) x sage: chebyshev_T.eval_formula(0,x) 1 sage: chebyshev_T.eval_formula(1,x) x sage: chebyshev_T.eval_formula(2,0.1) == chebyshev_T._evalf_(2,0.1) True sage: chebyshev_T.eval_formula(10,x) 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 sage: chebyshev_T.eval_algebraic(10,x).expand() 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 """ if n < 0: return self.eval_formula(-n, x) elif n == 0: return parent(x).one() res = parent(x).zero() for j in xrange(0, n // 2 + 1): f = factorial(n - 1 - j) / factorial(j) / factorial(n - 2 * j) res += (-1)**j * (2 * x)**(n - 2 * j) * f res *= n / 2 return res
def abnormal_polynomials(L, r, m, s): r""" Return the highest degree coefficients for abnormal polynomials in step `s` of layer `m` in the Lie algebra `L`. INPUT: - ``L`` -- the Lie algebra to do the computations in - ``r`` -- the rank of the free Lie algebra - ``m`` -- the layer whose abnormal polynomials are computed - ``s`` -- the step of coefficients to compute OUTPUT: A dictionary {X: {mon: coeff}, where - ``X`` -- a Hall basis element of layer `m` - ``mon`` -- a tuple `(i_1,...,i_n)` describing the monomial `x_1^{i_1}...x_n^{i_n}` - ``coeff`` -- an element of a :class:`HallQuotient` Lie algebra whose dual element would be the coefficient of the monomial in the polynomial `P_X` """ elems = sum((list(L.graded_basis(k)) for k in range(1, m)), []) weights = sum(([k] * len(L.graded_basis(k)) for k in range(1, m)), []) P = {} mons = list(reversed(list(WeightedIntegerVectors(s - m, weights)))) verbose("computing abnormal polynomials:") for X in L.graded_basis(m): PX = {} pname = freebasis_element_to_short_word(X).replace("X", "P") verbose(" %s deg %d coefficients" % (pname, s - m)) # compute coefficients for the abnormal polynomial P_X for mon in mons: # compute coefficient of the monomial mon adx = X mult = QQ(1) for i in mon: mult = mult / factorial(i) for rep, Xi in zip(mon, elems): for k in range(rep): adx = Xi.bracket(adx) PX[tuple(mon)] = mult * adx P[X] = PX return P
def cardinality(self): r""" Count the number of linear extensions using a hook-length formula. EXAMPLES:: sage: from sage.combinat.posets.poset_examples import Posets sage: P = Posets.YoungDiagramPoset(Partition([3,2]), dual=True) sage: P.linear_extensions().cardinality() 5 """ num_elmts = self._poset.cardinality() if num_elmts == 0: return 1 hook_product = self._poset.hook_product() return factorial(num_elmts) // hook_product
def __getitem__(self, key): """ EXAMPLES:: sage: a, b, c, z = var('a b c z') sage: hs = HypergeometricSeries([a, b], [c], z) sage: for i in range(4): print hs[i] 1 a*b*z/c 1/2*(b + 1)*(a + 1)*a*b*z^2/((c + 1)*c) 1/6*(b + 1)*(b + 2)*(a + 1)*(a + 2)*a*b*z^3/((c + 1)*(c + 2)*c) """ if key >= 0: nominator = PochhammerSymbol(self.list_a, key).evaluate() denominator = PochhammerSymbol(self.list_b, key).evaluate()*factorial(key) return nominator / denominator * self.z**key else: return 0
def kontsevich_star_product_terms(K, prime_weights, precision): """ Kontsevich star product terms in KontsevichGraphSums ``K`` up to order ``precision``. INPUT: - ``K`` - KontsevichGraphSums module. - ``prime_weights`` - weights of prime graphs, modulo edge labeling and mirror images. EXAMPLES:: sage: K = KontsevichGraphSums(QQ); sage: weights = {} sage: weights[KontsevichGraph({'F' : {},'G' : {}}, ground_vertices=('F','G'), immutable=True)] = 1 sage: weights[KontsevichGraph([(1, 'F', 'L'), (1, 'G', 'R')], ground_vertices=('F','G'), immutable=True)] = 1/2 sage: weights[KontsevichGraph([(1, 2, 'R'), (1, 'F', 'L'), (2, 1, 'L'), (2, 'G', 'R')], ground_vertices=('F','G'), immutable=True)] = 1/24 sage: weights[KontsevichGraph([(1, 2, 'R'), (1, 'F', 'L'), (2, 'F', 'L'), (2, 'G', 'R')], ground_vertices=('F','G'), immutable=True)] = 1/12 sage: S.<h> = KontsevichGraphSeriesRng(K, star_product_terms = kontsevich_star_product_terms(K, weights, 2), default_prec = 2); sage: F = S(KontsevichGraph(('F',),immutable=True)); sage: G = S(KontsevichGraph(('G',),immutable=True)); sage: H = S(KontsevichGraph(('H',),immutable=True)); sage: A = (F*G)*H - F*(G*H) sage: A.reduce() sage: len(A[2]) # three terms in the Jacobi identity 3 """ series_terms = { 0: K([(prime_weights[KontsevichGraph(('F', 'G'), immutable=True)], KontsevichGraph(('F', 'G'), immutable=True))]) } for n in range(1, precision + 1): term = K(0) for graph in kontsevich_graphs(n, modulo_edge_labeling=True, positive_differential_order=True): coeff = kontsevich_weight( graph, prime_weights) * graph.multiplicity() / factorial( len(graph.internal_vertices())) if coeff != 0: term += K([(coeff, graph)]) series_terms[n] = term return series_terms
def expansion_on_basis(self, w): r""" Return the expansion of `S_w` in words of the shuffle algebra. INPUT: - ``w`` -- a word EXAMPLES:: sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis() sage: S.expansion_on_basis(Word()) B[word: ] sage: S.expansion_on_basis(Word()).parent() Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field sage: S.expansion_on_basis(Word('abba')) 2*B[word: aabb] + B[word: abab] + B[word: abba] sage: S.expansion_on_basis(Word()) B[word: ] sage: S.expansion_on_basis(Word('abab')) 2*B[word: aabb] + B[word: abab] """ from sage.functions.other import factorial if len(w) == 0: return self._alg.one() if len(w) == 1: return self._alg.monomial(w) if w.is_lyndon(): W = self.basis().keys() letter = W(w[0]) expansion = self.expansion_on_basis(W(w[1:])) return self._alg.sum_of_terms([(letter * i, c) for i, c in expansion]) lf = w.lyndon_factorization() powers = {} for i in lf: powers[i] = powers.get(i, 0) + 1 denom = prod(factorial(p) for p in powers.values()) result = self._alg.prod(self.expansion_on_basis(i) for i in lf) return self._alg(result / denom)
def hodge_star(self, metric): r""" Compute the Hodge dual of the differential form. If ``self`` is a `p`-form `A`, its Hodge dual is the `(n-p)`-form `*A` defined by (`n` being the manifold's dimension) .. MATH:: *A_{i_1\ldots i_{n-p}} = \frac{1}{p!} A_{k_1\ldots k_p} \epsilon^{k_1\ldots k_p}_{\qquad\ i_1\ldots i_{n-p}} where `\epsilon` is the volume form associated with some pseudo-Riemannian metric `g` on the manifold, and the indices `k_1,\ldots, k_p` are raised with `g`. INPUT: - ``metric``: the pseudo-Riemannian metric `g` defining the Hodge dual, via the volume form `\epsilon`; must be an instance of :class:`~sage.geometry.manifolds.metric.Metric` OUTPUT: - the `(n-p)`-form `*A` EXAMPLES: """ from sage.functions.other import factorial from sage.tensor.modules.format_utilities import format_unop_txt, \ format_unop_latex p = self._tensor_rank eps = metric.volume_form(p) args = range(p) + [eps] + range(p) resu = self.contract(*args) if p > 1: resu = resu / factorial(p) resu.set_name(name=format_unop_txt('*', self._name), latex_name=format_unop_latex(r'\star ', self._latex_name)) return resu
def expansion_on_basis(self, w): r""" Return the expansion of `S_w` in words of the shuffle algebra. INPUT: - ``w`` -- a word EXAMPLES:: sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis() sage: S.expansion_on_basis(Word()) B[word: ] sage: S.expansion_on_basis(Word()).parent() Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field sage: S.expansion_on_basis(Word('abba')) 2*B[word: aabb] + B[word: abab] + B[word: abba] sage: S.expansion_on_basis(Word()) B[word: ] sage: S.expansion_on_basis(Word('abab')) 2*B[word: aabb] + B[word: abab] """ from sage.functions.other import factorial if len(w) == 0: return self._alg.one() if len(w) == 1: return self._alg.monomial(w) if w.is_lyndon(): W = self.basis().keys() letter = W(w[0]) expansion = self.expansion_on_basis(W(w[1:])) return self._alg.sum_of_terms([(letter * i, c) for i,c in expansion]) lf = w.lyndon_factorization() powers = {} for i in lf: powers[i] = powers.get(i, 0) + 1 denom = prod(factorial(p) for p in powers.values()) result = self._alg.prod(self.expansion_on_basis(i) for i in lf) return self._alg(result / denom)
def _eval_(self, s, x): r""" TESTS:: sage: hurwitz_zeta(x, 1) zeta(x) sage: hurwitz_zeta(4, 3) 1/90*pi^4 - 17/16 sage: hurwitz_zeta(-4, x) -1/5*x^5 + 1/2*x^4 - 1/3*x^3 + 1/30*x sage: hurwitz_zeta(3, 0.5) 8.41439832211716 """ if x == 1: return zeta(s) if s in ZZ and s > 1: return ((-1)**s) * psi(s - 1, x) / factorial(s - 1) elif s in ZZ and s < 0: return -bernoulli_polynomial(x, -s + 1) / (-s + 1) else: return
def order(self): r""" Returns the number of elements of ``self``. EXAMPLES:: sage: F.<a> = GF(4) sage: SemimonomialTransformationGroup(F, 5).order() == (4-1)**5 * factorial(5) * 2 True """ from sage.functions.other import factorial from sage.categories.homset import End n = self.degree() R = self.base_ring() if R.is_field(): multgroup_size = len(R) - 1 autgroup_size = R.degree() else: multgroup_size = R.unit_group_order() autgroup_size = len([x for x in End(R) if x.is_injective()]) return multgroup_size**n * factorial(n) * autgroup_size
def _eval_(self, s, x): r""" TESTS:: sage: hurwitz_zeta(x, 1) zeta(x) sage: hurwitz_zeta(4, 3) 1/90*pi^4 - 17/16 sage: hurwitz_zeta(-4, x) -1/5*x^5 + 1/2*x^4 - 1/3*x^3 + 1/30*x sage: hurwitz_zeta(3, 0.5) 8.41439832211716 """ if x == 1: return zeta(s) if s in ZZ and s > 1: return ((-1) ** s) * psi(s - 1, x) / factorial(s - 1) elif s in ZZ and s < 0: return -bernoulli_polynomial(x, -s + 1) / (-s + 1) else: return
def order(self): r""" Returns the number of elements of ``self``. EXAMPLES:: sage: F.<a> = GF(4) sage: SemimonomialTransformationGroup(F, 5).order() == (4-1)**5 * factorial(5) * 2 True """ from sage.functions.other import factorial from sage.categories.homset import End n = self.degree() R = self.base_ring() if R.is_field(): multgroup_size = len(R)-1 autgroup_size = R.degree() else: multgroup_size = R.unit_group_order() autgroup_size = len([x for x in End(R) if x.is_injective()]) return multgroup_size**n * factorial(n) * autgroup_size
def to_symmetric_function(self): r""" Take a function in the `\mathbf{w}` basis, and return its symmetric realization, when possible, expressed in the homogeneous basis of symmetric functions. OUTPUT: - If ``self`` is a symmetric function, then the expansion in the homogeneous basis of the symmetric functions is returned. Otherwise an error is raised. EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: elt = w[[1],[2,3]] + w[[1,2],[3]] + w[[1,3],[2]] sage: elt.to_symmetric_function() h[2, 1] sage: elt = w.sum_of_partitions([2,1,1]) / 2 sage: elt.to_symmetric_function() 1/2*h[2, 1, 1] TESTS:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: w(0).to_symmetric_function() 0 sage: w([]).to_symmetric_function() h[] sage: (2*w([])).to_symmetric_function() 2*h[] """ if not self.is_symmetric(): raise ValueError("not a symmetric function") h = SymmetricFunctions(self.parent().base_ring()).homogeneous() d = {A.shape(): c for A, c in self} return h.sum_of_terms( [(AA, cc / prod([factorial(_) for _ in AA.to_exp()])) for AA, cc in d.items()], distinct=True)
def idempotent(self, la): """ Return the idemponent corresponding to the partition ``la``. EXAMPLES:: sage: I = DescentAlgebra(QQ, 4).I() sage: E = I.idempotent([3,1]); E 1/2*I[1, 3] + 1/2*I[3, 1] sage: E*E == E True sage: E2 = I.idempotent([2,1,1]); E2 1/6*I[1, 1, 2] + 1/6*I[1, 2, 1] + 1/6*I[2, 1, 1] sage: E2*E2 == E2 True sage: E*E2 == I.zero() True """ from sage.combinat.permutation import Permutations k = len(la) C = Compositions(self.realization_of()._n) return self.sum_of_terms([(C(x), ~QQ(factorial(k))) for x in Permutations(la)])
def to_symmetric_function(self): r""" Take a function in the `\mathbf{w}` basis, and return its symmetric realization, when possible, expressed in the homogeneous basis of symmetric functions. OUTPUT: - If ``self`` is a symmetric function, then the expansion in the homogeneous basis of the symmetric functions is returned. Otherwise an error is raised. EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: elt = w[[1],[2,3]] + w[[1,2],[3]] + w[[1,3],[2]] sage: elt.to_symmetric_function() h[2, 1] sage: elt = w.sum_of_partitions([2,1,1]) / 2 sage: elt.to_symmetric_function() 1/2*h[2, 1, 1] TESTS:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: w(0).to_symmetric_function() 0 sage: w([]).to_symmetric_function() h[] sage: (2*w([])).to_symmetric_function() 2*h[] """ if not self.is_symmetric(): raise ValueError("not a symmetric function") h = SymmetricFunctions(self.parent().base_ring()).homogeneous() d = {A.shape(): c for A, c in self} return h.sum_of_terms( [(AA, cc / prod([factorial(_) for _ in AA.to_exp()])) for AA, cc in d.items()], distinct=True )
def coeff_sp(J, I): r""" Returns the coefficient `sp_{J,I}` as defined in [NCSF]_. INPUT: - ``J`` -- a composition - ``I`` -- a composition refining ``J`` OUTPUT: - integer EXAMPLES:: sage: from sage.combinat.ncsf_qsym.combinatorics import coeff_sp sage: coeff_sp(Composition([1,1,1]), Composition([2,1])) 2 sage: coeff_sp(Composition([2,1]), Composition([3])) 4 """ return prod(factorial(len(K)) * prod(K) for K in J.refinement_splitting(I))
def coeff_sp(J,I): r""" Returns the coefficient `sp_{J,I}` as defined in [NCSF]_. INPUT: - ``J`` -- a composition - ``I`` -- a composition refining ``J`` OUTPUT: - integer EXAMPLES:: sage: from sage.combinat.ncsf_qsym.combinatorics import coeff_sp sage: coeff_sp(Composition([1,1,1]), Composition([2,1])) 2 sage: coeff_sp(Composition([2,1]), Composition([3])) 4 """ return prod(factorial(len(K))*prod(K) for K in J.refinement_splitting(I))
def _eval_(self, s, x): r""" TESTS:: sage: hurwitz_zeta(x, 1) zeta(x) sage: hurwitz_zeta(4, 3) 1/90*pi^4 - 17/16 sage: hurwitz_zeta(-4, x) -1/5*x^5 + 1/2*x^4 - 1/3*x^3 + 1/30*x sage: hurwitz_zeta(3, 0.5) 8.41439832211716 """ co = get_coercion_model().canonical_coercion(s, x)[0] if is_inexact(co) and not isinstance(co, Expression): return self._evalf_(s, x, parent=parent(co)) if x == 1: return zeta(s) if s in ZZ and s > 1: return ((-1) ** s) * psi(s - 1, x) / factorial(s - 1) elif s in ZZ and s < 0: return -bernoulli_polynomial(x, -s + 1) / (-s + 1) else: return
def _eval_(self, s, x): r""" TESTS:: sage: hurwitz_zeta(x, 1) zeta(x) sage: hurwitz_zeta(4, 3) 1/90*pi^4 - 17/16 sage: hurwitz_zeta(-4, x) -1/5*x^5 + 1/2*x^4 - 1/3*x^3 + 1/30*x sage: hurwitz_zeta(3, 0.5) 8.41439832211716 """ co = get_coercion_model().canonical_coercion(s, x)[0] if is_inexact(co) and not isinstance(co, Expression): return self._evalf_(s, x, parent=parent(co)) if x == 1: return zeta(s) if s in ZZ and s > 1: return ((-1)**s) * psi(s - 1, x) / factorial(s - 1) elif s in ZZ and s < 0: return -bernoulli_polynomial(x, -s + 1) / (-s + 1) else: return
def kontsevich_star_product_terms(K, prime_weights, precision): """ Kontsevich star product terms in KontsevichGraphSums ``K`` up to order ``precision``. INPUT: - ``K`` - KontsevichGraphSums module. - ``prime_weights`` - weights of prime graphs, modulo edge labeling and mirror images. EXAMPLES:: sage: K = KontsevichGraphSums(QQ); sage: weights = {} sage: weights[KontsevichGraph({'F' : {},'G' : {}}, ground_vertices=('F','G'), immutable=True)] = 1 sage: weights[KontsevichGraph([(1, 'F', 'L'), (1, 'G', 'R')], ground_vertices=('F','G'), immutable=True)] = 1/2 sage: weights[KontsevichGraph([(1, 2, 'R'), (1, 'F', 'L'), (2, 1, 'L'), (2, 'G', 'R')], ground_vertices=('F','G'), immutable=True)] = 1/24 sage: weights[KontsevichGraph([(1, 2, 'R'), (1, 'F', 'L'), (2, 'F', 'L'), (2, 'G', 'R')], ground_vertices=('F','G'), immutable=True)] = 1/12 sage: S.<h> = KontsevichGraphSeriesRng(K, star_product_terms = kontsevich_star_product_terms(K, weights, 2), default_prec = 2); sage: F = S(KontsevichGraph(('F',),immutable=True)); sage: G = S(KontsevichGraph(('G',),immutable=True)); sage: H = S(KontsevichGraph(('H',),immutable=True)); sage: A = (F*G)*H - F*(G*H) sage: A.reduce() sage: len(A[2]) # three terms in the Jacobi identity 3 """ series_terms = {0 : K([(prime_weights[KontsevichGraph(('F','G'), immutable=True)], KontsevichGraph(('F','G'), immutable=True))])} for n in range(1, precision + 1): term = K(0) for graph in kontsevich_graphs(n, modulo_edge_labeling=True, positive_differential_order=True): coeff = kontsevich_weight(graph, prime_weights)*graph.multiplicity()/factorial(len(graph.internal_vertices())) if coeff != 0: term += K([(coeff, graph)]) series_terms[n] = term return series_terms
def __call__(self, x, type_3_pole_check=True): """ Makes dynamical systems on Berkovich space over ``Cp`` callable. INPUT: - ``x`` -- a point of projective Berkovich space over ``Cp``. - type_3_pole_check -- (default ``True``) A bool. WARNING: changing the value of type_3_pole_check can lead to mathematically incorrect answers. Only set to ``False`` if there are NO poles of the dynamical system in the disk corresponding to the type III point ``x``. See Examples. OUTPUT: A point of projective Berkovich space over ``Cp``. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(Qp(3), 1) sage: g = DynamicalSystem_projective([x^2 + y^2, x*y]) sage: G = DynamicalSystem_Berkovich(g) sage: B = G.domain() sage: Q3 = B(0, 1) sage: G(Q3) Type II point centered at (0 : 1 + O(3^20)) of radius 3^0 :: sage: P.<x,y> = ProjectiveSpace(Qp(3), 1) sage: H = DynamicalSystem_Berkovich([x*y^2, x^3 + 20*y^3]) sage: B = H.domain() sage: Q4 = B(1/9, 1.5) sage: H(Q4, False) Type III point centered at (3^4 + 3^10 + 2*3^11 + 2*3^13 + 2*3^14 + 2*3^15 + 3^17 + 2*3^18 + 2*3^19 + 3^20 + 3^21 + 3^22 + O(3^24) : 1 + O(3^20)) of radius 0.00205761316872428 ALGORITHM: - For type II points, we use the approach outlined in Example 7.37 of [Ben2019]_ - For type III points, we use Proposition 7.6 of [Ben2019]_ """ if not isinstance(x.parent(), Berkovich_Cp_Projective): try: x = self.domain()(x) except: raise TypeError( 'action of dynamical system not defined on %s' % x.parent()) if x.parent().is_padic_base() != self.domain().is_padic_base(): raise ValueError('x was not backed by the same type of field as f') if x.prime() != self.domain().prime(): raise ValueError( 'x and f are defined over Berkovich spaces over Cp for different p' ) if x.type_of_point() == 1: return self.domain()(self._system(x.center())) if x.type_of_point() == 4: raise NotImplementedError( 'action on Type IV points not implemented') f = self._system if x.type_of_point() == 2: if self.domain().is_number_field_base(): ideal = self.domain().ideal() ring_of_integers = self.domain().base_ring().ring_of_integers() field = f.domain().base_ring() M = Matrix([[field(x.prime()**(-1 * x.power())), x.center()[0]], [field(0), field(1)]]) F = list(f * M) R = field['z'] S = f.domain().coordinate_ring() z = R.gen(0) dehomogenize_hom = S.hom([z, 1]) for i in range(len(F)): F[i] = dehomogenize_hom(F[i]) lcm = field(1) for poly in F: for i in poly: if i != 0: lcm = i.denominator().lcm(lcm) for i in range(len(F)): F[i] *= lcm gcd = [i for i in F[0] if i != 0][0] for poly in F: for i in poly: if i != 0: gcd = gcd * i * gcd.lcm(i).inverse_of_unit() for i in range(len(F)): F[i] *= gcd.inverse_of_unit() gcd = F[0].gcd(F[1]) F[0] = F[0].quo_rem(gcd)[0] F[1] = F[1].quo_rem(gcd)[0] fraction = [] for poly in F: new_poly = [] for i in poly: if self.domain().is_padic_base(): new_poly.append(i.residue()) else: new_poly.append(ring_of_integers(i).mod(ideal)) new_poly = R(new_poly) fraction.append((new_poly)) gcd = fraction[0].gcd(fraction[1]) num = fraction[0].quo_rem(gcd)[0] dem = fraction[1].quo_rem(gcd)[0] if dem.is_zero(): f = DynamicalSystem_affine(F[0] / F[1]).homogenize(1) f = f.conjugate(Matrix([[0, 1], [1, 0]])) g = DynamicalSystem_Berkovich(f) return g(self.domain()(QQ(0), QQ(1))).involution_map() # if the reduction is not constant, the image is the Gauss point if not (num.is_constant() and dem.is_constant()): return self.domain()(QQ(0), QQ(1)) if self.domain().is_padic_base(): reduced_value = field(num * dem.inverse_of_unit()).lift_to_precision( field.precision_cap()) else: reduced_value = field(num * dem.inverse_of_unit()) new_num = F[0] - reduced_value * F[1] if self.domain().is_padic_base(): power_of_p = min([i.valuation() for i in new_num]) else: power_of_p = min([i.valuation(ideal) for i in new_num]) inverse_map = field(x.prime()**power_of_p) * z + reduced_value if self.domain().is_padic_base(): return self.domain()(inverse_map(0), (inverse_map(1) - inverse_map(0)).abs()) else: val = (inverse_map(1) - inverse_map(0)).valuation(ideal) if val == Infinity: return self.domain()(inverse_map(0), 0) return self.domain()(inverse_map(0), x.prime()**(-1 * val)) # point is now type III, so we compute using Proposition 7.6 [of Benedetto] affine_system = f.dehomogenize(1) dem = affine_system.defining_polynomials()[0].denominator( ).univariate_polynomial() if type_3_pole_check: if self.domain().is_padic_base(): factorization = [i[0] for i in dem.factor()] for factor in factorization: if factor.degree() >= 2: try: factor_root_field = factor.root_field('a') factor = factor.change_ring(factor_root_field) except: raise NotImplementedError( 'cannot check if poles lie in type III disk') else: factor_root_field = factor.base_ring() center = factor_root_field(x.center()[0]) for pole in [i[0] for i in factor.roots()]: if (center - pole).abs() <= x.radius(): raise NotImplementedError( 'image of type III point not implemented when poles in disk' ) else: dem_splitting_field, embedding = dem.splitting_field('a', True) poles = [i[0] for i in dem.roots(dem_splitting_field)] primes_above = dem_splitting_field.primes_above( self.domain().ideal()) # check if all primes of the extension map the roots to outside # the disk corresponding to the type III point for prime in primes_above: no_poles = True for pole in poles: valuation = (embedding(x.center()[0]) - pole).valuation(prime) if valuation == Infinity: no_poles = False break elif x.prime()**(-1 * valuation / prime.absolute_ramification_index() ) <= x.radius(): no_poles = False break if not no_poles: break if not no_poles: raise NotImplementedError( 'image of type III not implemented when poles in disk') nth_derivative = f.dehomogenize(1).defining_polynomials()[0] variable = nth_derivative.parent().gens()[0] a = x.center()[0] Taylor_expansion = [] from sage.functions.other import factorial for i in range(f.degree() + 1): Taylor_expansion.append(nth_derivative(a) * 1 / factorial(i)) nth_derivative = nth_derivative.derivative(variable) r = x.radius() new_center = f(a) if self.domain().is_padic_base(): new_radius = max([ Taylor_expansion[i].abs() * r**i for i in range(1, len(Taylor_expansion)) ]) else: if prime is None: prime = x.parent().ideal() dem_splitting_field = x.parent().base_ring() p = x.prime() new_radius = 0 for i in range(1, len(Taylor_expansion)): valuation = dem_splitting_field( Taylor_expansion[i]).valuation(prime) new_radius = max( new_radius, p**(-valuation / prime.absolute_ramification_index()) * r**i) return self.domain()(new_center, new_radius)
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 f(partition): n = partition.size() return (StandardTableaux(partition).cardinality() * t**n / factorial(n))
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 hodge_star(self, metric): r""" Compute the Hodge dual of the differential form. If ``self`` is a `p`-form `A`, its Hodge dual is the `(n-p)`-form `*A` defined by (`n` being the manifold's dimension) .. MATH:: *A_{i_1\ldots i_{n-p}} = \frac{1}{p!} A_{k_1\ldots k_p} \epsilon^{k_1\ldots k_p}_{\qquad\ i_1\ldots i_{n-p}} where $\epsilon$ is the volume form associated with some pseudo-Riemannian metric `g` on the manifold, and the indices `k_1,\ldots, k_p` are raised with `g`. INPUT: - ``metric``: the pseudo-Riemannian metric `g` defining the Hodge dual, via the volume form `\epsilon`; must be an instance of :class:`Metric` OUTPUT: - the `(n-p)`-form `*A` EXAMPLES: Hodge star of a 1-form in the Euclidean space `R^3`:: sage: m = Manifold(3, 'M', start_index=1) sage: X = Chart(m, 'x y z', 'xyz') sage: g = Metric(m, 'g') sage: g[1,1], g[2,2], g[3,3] = 1, 1, 1 sage: a = OneForm(m, 'A') sage: var('Ax Ay Az') (Ax, Ay, Az) sage: a[:] = (Ax, Ay, Az) sage: sa = a.hodge_star(g) ; sa 2-form '*A' on the 3-dimensional manifold 'M' sage: sa.show() *A = Az dx/\dy - Ay dx/\dz + Ax dy/\dz sage: ssa = sa.hodge_star(g) ; ssa 1-form '**A' on the 3-dimensional manifold 'M' sage: ssa.show() **A = Ax dx + Ay dy + Az dz sage: ssa == a # must hold for a Riemannian metric in dimension 3 True Hodge star of a 0-form (scalar field) in `R^3`:: sage: f = ScalarField(m, function('F',x,y,z), name='f') sage: sf = f.hodge_star(g) ; sf 3-form '*f' on the 3-dimensional manifold 'M' sage: sf.show() *f = F(x, y, z) dx/\dy/\dz sage: ssf = sf.hodge_star(g) ; ssf scalar field '**f' on the 3-dimensional manifold 'M' sage: ssf.show() **f: (x, y, z) |--> F(x, y, z) sage: ssf == f # must hold for a Riemannian metric True Hodge star of a 0-form in Minkowksi spacetime:: sage: m = Manifold(4, 'M') sage: X = Chart(m, 't x y z', 'txyz') sage: g = Metric(m, 'g', signature=2) sage: g[0,0], g[1,1], g[2,2], g[3,3] = -1, 1, 1, 1 sage: g.show() # Minkowski metric g = -dt*dt + dx*dx + dy*dy + dz*dz sage: var('f0') f0 sage: f = ScalarField(m, f0, name='f') sage: sf = f.hodge_star(g) ; sf 4-form '*f' on the 4-dimensional manifold 'M' sage: sf.show() *f = f0 dt/\dx/\dy/\dz sage: ssf = sf.hodge_star(g) ; ssf scalar field '**f' on the 4-dimensional manifold 'M' sage: ssf.show() **f: (t, x, y, z) |--> -f0 sage: ssf == -f # must hold for a Lorentzian metric True Hodge star of a 1-form in Minkowksi spacetime:: sage: a = OneForm(m, 'A') sage: var('At Ax Ay Az') (At, Ax, Ay, Az) sage: a[:] = (At, Ax, Ay, Az) sage: a.show() A = At dt + Ax dx + Ay dy + Az dz sage: sa = a.hodge_star(g) ; sa 3-form '*A' on the 4-dimensional manifold 'M' sage: sa.show() *A = -Az dt/\dx/\dy + Ay dt/\dx/\dz - Ax dt/\dy/\dz - At dx/\dy/\dz sage: ssa = sa.hodge_star(g) ; ssa 1-form '**A' on the 4-dimensional manifold 'M' sage: ssa.show() **A = At dt + Ax dx + Ay dy + Az dz sage: ssa == a # must hold for a Lorentzian metric in dimension 4 True Hodge star of a 2-form in Minkowksi spacetime:: sage: F = DiffForm(m, 2, 'F') sage: var('Ex Ey Ez Bx By Bz') (Ex, Ey, Ez, Bx, By, Bz) sage: F[0,1], F[0,2], F[0,3] = -Ex, -Ey, -Ez sage: F[1,2], F[1,3], F[2,3] = Bz, -By, Bx sage: F[:] [ 0 -Ex -Ey -Ez] [ Ex 0 Bz -By] [ Ey -Bz 0 Bx] [ Ez By -Bx 0] sage: sF = F.hodge_star(g) ; sF 2-form '*F' on the 4-dimensional manifold 'M' sage: sF[:] [ 0 Bx By Bz] [-Bx 0 Ez -Ey] [-By -Ez 0 Ex] [-Bz Ey -Ex 0] sage: ssF = sF.hodge_star(g) ; ssF 2-form '**F' on the 4-dimensional manifold 'M' sage: ssF[:] [ 0 Ex Ey Ez] [-Ex 0 -Bz By] [-Ey Bz 0 -Bx] [-Ez -By Bx 0] sage: ssF.show() **F = Ex dt/\dx + Ey dt/\dy + Ez dt/\dz - Bz dx/\dy + By dx/\dz - Bx dy/\dz sage: F.show() F = -Ex dt/\dx - Ey dt/\dy - Ez dt/\dz + Bz dx/\dy - By dx/\dz + Bx dy/\dz sage: ssF == -F # must hold for a Lorentzian metric in dimension 4 True Test of the standard identity .. MATH:: *(A\wedge B) = \epsilon(A^\sharp, B^\sharp, ., .) where `A` and `B` are any 1-forms and `A^\sharp` and `B^\sharp` the vectors associated to them by the metric `g` (index raising):: sage: b = OneForm(m, 'B') sage: var('Bt Bx By Bz') (Bt, Bx, By, Bz) sage: b[:] = (Bt, Bx, By, Bz) ; b.show() B = Bt dt + Bx dx + By dy + Bz dz sage: epsilon = g.volume_form() sage: (a.wedge(b)).hodge_star(g) == epsilon.contract(0, a.up(g), 0).contract(0, b.up(g), 0) True """ from sage.functions.other import factorial from utilities import format_unop_txt, format_unop_latex p = self.rank eps = metric.volume_form(p) if p == 0: resu = self * eps else: resu = self.contract(0, eps, 0) for j in range(1, p): resu = resu.self_contract(0, p - j) if p > 1: resu = resu / factorial(p) # Name and LaTeX name of the result: resu.name = format_unop_txt('*', self.name) resu.latex_name = format_unop_latex(r'\star ', self.latex_name) return resu
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 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 cardinality(self): r""" Return the number of linear extensions by using the determinant formula for counting linear extensions of mobiles. EXAMPLES:: sage: from sage.combinat.posets.mobile import MobilePoset sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4, ....: 3), (5,4),(5,6),(7,4),(7,8)]])) sage: M.linear_extensions().cardinality() 1098 sage: M1 = posets.RibbonPoset(6, [1,3]) sage: M1.linear_extensions().cardinality() 61 sage: P = posets.MobilePoset(posets.RibbonPoset(7, [1,3]), {1: ....: [posets.YoungDiagramPoset([3, 2], dual=True)], 3: [posets.DoubleTailedDiamond(6)]}, ....: anchor=(4, 2, posets.ChainPoset(6))) sage: P.linear_extensions().cardinality() 361628701868606400 """ import sage.combinat.posets.d_complete as dc # Find folds if self._poset._anchor: anchor_index = self._poset._ribbon.index(self._poset._anchor[0]) else: anchor_index = len(self._poset._ribbon) folds_up = [] folds_down = [] for ind, r in enumerate(self._poset._ribbon[:-1]): if ind < anchor_index and self._poset.is_greater_than( r, self._poset._ribbon[ind + 1]): folds_up.append((self._poset._ribbon[ind + 1], r)) elif ind >= anchor_index and self._poset.is_less_than( r, self._poset._ribbon[ind + 1]): folds_down.append((r, self._poset._ribbon[ind + 1])) if not folds_up and not folds_down: return dc.DCompletePoset( self._poset).linear_extensions().cardinality() # Get ordered connected components cr = self._poset.cover_relations() foldless_cr = [ tuple(c) for c in cr if tuple(c) not in folds_up and tuple(c) not in folds_down ] elmts = list(self._poset._elements) poset_components = DiGraph([elmts, foldless_cr]) ordered_poset_components = [ poset_components.connected_component_containing_vertex(f[1], sort=False) for f in folds_up ] ordered_poset_components.extend( poset_components.connected_component_containing_vertex(f[0], sort=False) for f in folds_down) ordered_poset_components.append( poset_components.connected_component_containing_vertex( folds_down[-1][1] if folds_down else folds_up[-1][0], sort=False)) # Return determinant # Consoludate the folds lists folds = folds_up folds.extend(folds_down) mat = [] for i in range(len(folds) + 1): mat_poset = dc.DCompletePoset( self._poset.subposet(ordered_poset_components[i])) row = [0] * (i - 1 if i - 1 > 0 else 0) + [1 ] * (1 if i >= 1 else 0) row.append(1 / mat_poset.hook_product()) for j, f in enumerate(folds[i:]): next_poset = self._poset.subposet( ordered_poset_components[j + i + 1]) mat_poset = dc.DCompletePoset( next_poset.slant_sum(mat_poset, f[0], f[1])) row.append(1 / mat_poset.hook_product()) mat.append(row) return matrix(QQ, mat).determinant() * factorial( self._poset.cardinality())
def hodge_star(self, metric): r""" Compute the Hodge dual of the differential form. If ``self`` is a `p`-form `A`, its Hodge dual is the `(n-p)`-form `*A` defined by (`n` being the manifold's dimension) .. MATH:: *A_{i_1\ldots i_{n-p}} = \frac{1}{p!} A_{k_1\ldots k_p} \epsilon^{k_1\ldots k_p}_{\qquad\ i_1\ldots i_{n-p}} where $\epsilon$ is the volume form associated with some pseudo-Riemannian metric `g` on the manifold, and the indices `k_1,\ldots, k_p` are raised with `g`. INPUT: - ``metric``: the pseudo-Riemannian metric `g` defining the Hodge dual, via the volume form `\epsilon`; must be an instance of :class:`Metric` OUTPUT: - the `(n-p)`-form `*A` EXAMPLES: Hodge star of a 1-form in the Euclidean space `R^3`:: sage: m = Manifold(3, 'M', start_index=1) sage: X.<x,y,z> = m.chart('x y z', 'xyz') sage: g = Metric(m, 'g') sage: g[1,1], g[2,2], g[3,3] = 1, 1, 1 sage: a = OneForm(m, 'A') sage: var('Ax Ay Az') (Ax, Ay, Az) sage: a[:] = (Ax, Ay, Az) sage: sa = a.hodge_star(g) ; sa 2-form '*A' on the 3-dimensional manifold 'M' sage: sa.view() *A = Az dx/\dy - Ay dx/\dz + Ax dy/\dz sage: ssa = sa.hodge_star(g) ; ssa 1-form '**A' on the 3-dimensional manifold 'M' sage: ssa.view() **A = Ax dx + Ay dy + Az dz sage: ssa == a # must hold for a Riemannian metric in dimension 3 True Hodge star of a 0-form (scalar field) in `R^3`:: sage: f = ScalarField(m, function('F',x,y,z), name='f') sage: sf = f.hodge_star(g) ; sf 3-form '*f' on the 3-dimensional manifold 'M' sage: sf.view() *f = F(x, y, z) dx/\dy/\dz sage: ssf = sf.hodge_star(g) ; ssf scalar field '**f' on the 3-dimensional manifold 'M' sage: ssf.view() **f: (x, y, z) |--> F(x, y, z) sage: ssf == f # must hold for a Riemannian metric True Hodge star of a 0-form in Minkowksi spacetime:: sage: m = Manifold(4, 'M') sage: X = m.chart('t x y z', 'txyz') sage: g = Metric(m, 'g', signature=2) sage: g[0,0], g[1,1], g[2,2], g[3,3] = -1, 1, 1, 1 sage: g.view() # Minkowski metric g = -dt*dt + dx*dx + dy*dy + dz*dz sage: var('f0') f0 sage: f = ScalarField(m, f0, name='f') sage: sf = f.hodge_star(g) ; sf 4-form '*f' on the 4-dimensional manifold 'M' sage: sf.view() *f = f0 dt/\dx/\dy/\dz sage: ssf = sf.hodge_star(g) ; ssf scalar field '**f' on the 4-dimensional manifold 'M' sage: ssf.view() **f: (t, x, y, z) |--> -f0 sage: ssf == -f # must hold for a Lorentzian metric True Hodge star of a 1-form in Minkowksi spacetime:: sage: a = OneForm(m, 'A') sage: var('At Ax Ay Az') (At, Ax, Ay, Az) sage: a[:] = (At, Ax, Ay, Az) sage: a.view() A = At dt + Ax dx + Ay dy + Az dz sage: sa = a.hodge_star(g) ; sa 3-form '*A' on the 4-dimensional manifold 'M' sage: sa.view() *A = -Az dt/\dx/\dy + Ay dt/\dx/\dz - Ax dt/\dy/\dz - At dx/\dy/\dz sage: ssa = sa.hodge_star(g) ; ssa 1-form '**A' on the 4-dimensional manifold 'M' sage: ssa.view() **A = At dt + Ax dx + Ay dy + Az dz sage: ssa == a # must hold for a Lorentzian metric in dimension 4 True Hodge star of a 2-form in Minkowksi spacetime:: sage: F = DiffForm(m, 2, 'F') sage: var('Ex Ey Ez Bx By Bz') (Ex, Ey, Ez, Bx, By, Bz) sage: F[0,1], F[0,2], F[0,3] = -Ex, -Ey, -Ez sage: F[1,2], F[1,3], F[2,3] = Bz, -By, Bx sage: F[:] [ 0 -Ex -Ey -Ez] [ Ex 0 Bz -By] [ Ey -Bz 0 Bx] [ Ez By -Bx 0] sage: sF = F.hodge_star(g) ; sF 2-form '*F' on the 4-dimensional manifold 'M' sage: sF[:] [ 0 Bx By Bz] [-Bx 0 Ez -Ey] [-By -Ez 0 Ex] [-Bz Ey -Ex 0] sage: ssF = sF.hodge_star(g) ; ssF 2-form '**F' on the 4-dimensional manifold 'M' sage: ssF[:] [ 0 Ex Ey Ez] [-Ex 0 -Bz By] [-Ey Bz 0 -Bx] [-Ez -By Bx 0] sage: ssF.view() **F = Ex dt/\dx + Ey dt/\dy + Ez dt/\dz - Bz dx/\dy + By dx/\dz - Bx dy/\dz sage: F.view() F = -Ex dt/\dx - Ey dt/\dy - Ez dt/\dz + Bz dx/\dy - By dx/\dz + Bx dy/\dz sage: ssF == -F # must hold for a Lorentzian metric in dimension 4 True Test of the standard identity .. MATH:: *(A\wedge B) = \epsilon(A^\sharp, B^\sharp, ., .) where `A` and `B` are any 1-forms and `A^\sharp` and `B^\sharp` the vectors associated to them by the metric `g` (index raising):: sage: b = OneForm(m, 'B') sage: var('Bt Bx By Bz') (Bt, Bx, By, Bz) sage: b[:] = (Bt, Bx, By, Bz) ; b.view() B = Bt dt + Bx dx + By dy + Bz dz sage: epsilon = g.volume_form() sage: (a.wedge(b)).hodge_star(g) == epsilon.contract(0, a.up(g), 0).contract(0, b.up(g), 0) True """ from sage.functions.other import factorial from utilities import format_unop_txt, format_unop_latex p = self.rank eps = metric.volume_form(p) if p == 0: resu = self * eps else: resu = self.contract(0, eps, 0) for j in range(1, p): resu = resu.self_contract(0, p-j) if p > 1: resu = resu / factorial(p) # Name and LaTeX name of the result: resu.name = format_unop_txt('*', self.name) resu.latex_name = format_unop_latex(r'\star ', self.latex_name) return resu