def _eval_(self, n, m, theta, phi, **kwargs): r""" TESTS:: sage: x, y = var('x y') sage: spherical_harmonic(1, 2, x, y) 0 sage: spherical_harmonic(1, -2, x, y) 0 sage: spherical_harmonic(1/2, 2, x, y) spherical_harmonic(1/2, 2, x, y) sage: spherical_harmonic(3, 2, x, y) 1/8*sqrt(30)*sqrt(7)*cos(x)*e^(2*I*y)*sin(x)^2/sqrt(pi) sage: spherical_harmonic(3, 2, 1, 2) 1/8*sqrt(30)*sqrt(7)*cos(1)*e^(4*I)*sin(1)^2/sqrt(pi) sage: spherical_harmonic(3 + I, 2., 1, 2) -0.351154337307488 - 0.415562233975369*I Check that :trac:`20939` is fixed:: sage: ex = spherical_harmonic(3,2,1,2*pi/3) sage: QQbar(ex * sqrt(pi)/cos(1)/sin(1)^2).minpoly() x^4 + 105/32*x^2 + 11025/1024 """ if n in ZZ and m in ZZ and n > -1: if abs(m) > n: return ZZ(0) if m == 0 and theta.is_zero(): return sqrt((2*n+1)/4/pi) from sage.arith.misc import factorial from sage.functions.trig import cos from sage.functions.orthogonal_polys import gen_legendre_P return (sqrt(factorial(n-m) * (2*n+1) / (4*pi * factorial(n+m))) * exp(I*m*phi) * gen_legendre_P(n, m, cos(theta)) * (-1)**m).simplify_trig()
def f(partition): n = 0 for part in partition: if part != 1: return 0 n += 1 return t**n / factorial(n)
def minimal_strata_spin_diff(gmax, rational=False): r""" Return the differences of volumes between even and odd components in H(2g-2) for the genus `g` going from ``1`` up to ``gmax-1``. If there are no even/odd components, the corresponding total volume is 0. Formulas are from [CheMoeSauZag20]_. EXAMPLES:: sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import minimal_strata_spin_diff sage: minimal_strata_spin_diff(5) [-1/3*pi^2, -1/120*pi^4, -143/544320*pi^6, -15697/1959552000*pi^8] sage: minimal_strata_spin_diff(5, rational=True) [-2, -3/4, -143/576, -15697/207360] """ n = 2 * gmax R = QQ['u'] u = R.gen() # B(u) = formula (15) in [CMSZ20] B = (2 * (u/2)._sinh_series(n).shift(-1)).inverse_series_trunc(n) # Pz and a in section 6.3 of [CMSZ20] Pz = (sum(-bernoulli(2*j) * u**(2*j) / (2*j) / 2**j for j in range(1, n // 2)))._exp_series(n) a = ((u*Pz.inverse_series_trunc(n)).revert_series(n).shift(-1) ).inverse_series_trunc(n) # theorem 6.11 in [CMSZ20], normalized volume v(2g-2)=(2g-1)*Vol(2g-2), note the missing factor 2 if rational: return [2* (-2) * a[2*g] * 2 * g / (2*g - 1) / bernoulli(2*g) for g in range(1, gmax)] else: return [2* (-1)**(g) * (2*pi)**(2*g) * a[2 * g] /(2*g - 1) / factorial(2*g - 1) for g in range(1, gmax)]
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 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 minimal_strata_CMSZ(gmax, rational=False): r""" Return the volumes of `\cH(2g-2)` for the genus `g` going from ``1`` up to ``gmax-1``. The algorithm is the one from Sauvaget [Sau18]_ involving an implicit equation. As explained in [CheMoeSauZag20]_, one could go through Lagrange inversion. Note that they miss factor 2 in their theorem 4.1. EXAMPLES:: sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import minimal_strata_CMSZ sage: minimal_strata_CMSZ(6, True) [2, 3/4, 305/576, 87983/207360, 1019547/2867200] sage: minimal_strata_CMSZ(6, False) [1/3*pi^2, 1/120*pi^4, 61/108864*pi^6, 12569/279936000*pi^8, 12587/3311616000*pi^10] sage: from surface_dynamics import AbelianStratum sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import masur_veech_volume sage: for rat in [True, False]: ....: V0, V2, V4, V6 = minimal_strata_CMSZ(5, rational=rat) ....: MV0 = masur_veech_volume(AbelianStratum(0), rat, 'table') ....: assert V0 == MV0, (V0, MV0, rat) ....: MV2 = masur_veech_volume(AbelianStratum(2), rat, 'table') ....: assert V2 == MV2, (V2, MV2, rat) ....: MV4 = masur_veech_volume(AbelianStratum(4), rat, 'table') ....: assert V4 == MV4, (V4, MV4, rat) ....: MV6 = masur_veech_volume(AbelianStratum(6), rat, 'table') ....: assert V6 == MV6, (V6, MV6, rat) """ n = 2 * gmax - 1 R = QQ['u'] u = R.gen() # B(u) = formula (15) in [CMSZ20] B = (2 * (u/2)._sinh_series(n+1).shift(-1)).inverse_series_trunc(n+1) Q = u * (sum(factorial(j-1) * B[j] * u**(j) for j in range(1,n)))._exp_series(n+1) # A = formula (14) in [CSMZ20] tA = Q.revert_series(n+1).shift(-1).inverse_series_trunc(n) # normalized values of volumes in [CMSZ20] are # v(m_1, ..., m_n) = (2m_1+1) (2m_2+1) ... (2m_n+1) Vol(m_1, m_2, ..., m_n) if rational: return [-4 * (2*g) / ZZ(2*g-1) / bernoulli(2*g) * tA[2*g] for g in range(1,gmax)] else: return [2 * (2*pi)**(2*g) * (-1)**g / ZZ(2*g-1) / factorial(2*g - 1) * tA[2*g] for g in range(1,gmax)]
def _gs_term(self, base_ring): """ EXAMPLES:: sage: F = species.CharacteristicSpecies(2) sage: F.generating_series().coefficients(5) [0, 0, 1/2, 0, 0] sage: F.generating_series().count(2) 1 """ return base_ring(self._weight) / base_ring(factorial(self._n))
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 _gs_iterator(self, base_ring): r""" EXAMPLES:: sage: P = species.PartitionSpecies() sage: g = P.generating_series() sage: g.coefficients(5) [1, 1, 1, 5/6, 5/8] """ from sage.combinat.combinat import bell_number for n in _integers_from(0): yield self._weight * base_ring(bell_number(n) / factorial(n))
def minimal_strata_hyp(g, rational=False): r""" Return the volume of the hyperelliptic component H^{hyp}(2g-2). The explicit formula appears in section 6.5 of [CheMoeSauZag20]_. EXAMPLES:: sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import minimal_strata_hyp sage: minimal_strata_hyp(2) 1/120*pi^4 sage: minimal_strata_hyp(4) 1/580608*pi^8 sage: minimal_strata_hyp(10) 1/137733277917118464000*pi^20 sage: minimal_strata_hyp(10, rational=True) 668525/10499279483305984 """ if rational: return (-1)**(g+1) * 4 * factorial(2*g) / ( (2*g-1)*2*g*(2*g+1) * 2**(4*g-2) * bernoulli(2*g) * factorial(g-1)**2 ) else: return 2*pi**(2*g) / ( (2*g-1)*2*g*(2*g+1) * 2**(2*g-2) * factorial(g-1)**2 )
def count(self, n): """ Return the number of structures of size ``n``. EXAMPLES:: sage: from sage.combinat.species.generating_series import ExponentialGeneratingSeriesRing sage: R = ExponentialGeneratingSeriesRing(QQ) sage: f = R([1]) sage: [f.count(i) for i in range(7)] [1, 1, 2, 6, 24, 120, 720] """ return factorial(n) * self.coefficient(n)
def _gs_iterator(self, base_ring): """ The generating series for the species of subsets is `e^{2x}`. EXAMPLES:: sage: S = species.SubsetSpecies() sage: S.generating_series().coefficients(5) [1, 2, 2, 4/3, 2/3] """ for n in _integers_from(0): yield base_ring(2)**n / base_ring(factorial(n))
def _card(self, n): r""" Return the number of structures on an underlying set of size ``n`` for the species associated with ``self``. This is just ``n!`` times the coefficient of ``p[1]n`` in ``self``. EXAMPLES:: sage: cis = species.PartitionSpecies().cycle_index_series() sage: cis._card(4) 15 """ p = self.coefficient(n) return factorial(n) * p.coefficient([1] * n)
def _gs_iterator(self, base_ring): r""" The generating series for the species of sets is given by `e^x`. EXAMPLES:: sage: S = species.SetSpecies() sage: g = S.generating_series() sage: g.coefficients(10) [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040, 1/40320, 1/362880] sage: [g.count(i) for i in range(10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ for n in _integers_from(0): yield base_ring(self._weight / factorial(n))
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 write(self, g, n, s=None): r""" Print normalized values of the polynomials F_{g,n} """ g = ZZ(g) n = ZZ(n) if n < 0: raise ValueError if s is None: s = range(3 * g - 3 + n + 1) elif isinstance(s, numbers.Integral): s = [s] for t in s: for p in Partitions(t + n, length=n): p = [i - 1 for i in p] c = prod(factorial(2 * i + 1) for i in p) f = self.F(g, n, p) if f: print("{} {}".format([i for i in p], self.F(g, n, p) / c))
def _functorial_compose_gen(self, y, ao): """ Returns a generator for the coefficients of the functorial composition of self with y. EXAMPLES:: sage: E = species.SetSpecies() sage: E2 = E.restricted(min=2, max=3) sage: WP = species.SubsetSpecies() sage: P2 = E2*E sage: g1 = WP.generating_series() sage: g2 = P2.generating_series() sage: g = g1._functorial_compose_gen(g2, 0) sage: [next(g) for i in range(10)] [1, 1, 1, 4/3, 8/3, 128/15, 2048/45, 131072/315, 2097152/315, 536870912/2835] """ n = 0 while True: yield self.count(y.count(n)) / factorial(n) n += 1
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 idempotent(self, la): """ Return the idempotent corresponding to the partition ``la`` of `n`. 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((1, factorial(k)))) for x in Permutations(la)])
def f(partition): n = partition.size() return (StandardTableaux(partition).cardinality() * t**n / factorial(n))
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())