def cardinality(self): """ Returns the number of Lyndon words with the evaluation e. EXAMPLES:: sage: LyndonWords([]).cardinality() 0 sage: LyndonWords([2,2]).cardinality() 1 sage: LyndonWords([2,3,2]).cardinality() 30 Check to make sure that the count matches up with the number of Lyndon words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2]] + Compositions(4).list() sage: lws = [LyndonWords(comp) for comp in comps] sage: all(lw.cardinality() == len(lw.list()) for lw in lws) True """ evaluation = self._e le = builtins.list(evaluation) if len(evaluation) == 0: return 0 n = sum(evaluation) return sum([ moebius(j) * factorial(n / j) / prod([factorial(ni / j) for ni in evaluation]) for j in divisors(gcd(le)) ]) / n
def zero_eigenvectors(self, tg, flags): if self._use_symmetry: return self.symm_zero_eigenvectors(tg, flags) cn = self._graph.n s = tg.n k = flags[0].n # assume all flags the same order rows = [] for tv in Tuples(range(1, cn + 1), s): it = self._graph.degenerate_induced_subgraph(tv) using_phantom_edge = False phantom_edge = None if hasattr(self, "_phantom_edge") and it.ne == tg.ne - 1: extra_edges = [e for e in tg if not e in it] if len(extra_edges) == 1: phantom_edge = extra_edges[0] if all(tv[phantom_edge[i] - 1] == self._phantom_edge[i] for i in range(tg.r)): it.add_edge(phantom_edge) using_phantom_edge = True if not (using_phantom_edge or it.is_labelled_isomorphic(tg)): continue total = Integer(0) row = [0] * len(flags) for ov in UnorderedTuples(range(1, cn + 1), k - s): factor = factorial(k - s) for i in range(1, cn + 1): factor /= factorial(ov.count(i)) if self._weights: for v in ov: factor *= self._weights[v - 1] ig = self._graph.degenerate_induced_subgraph(tv + ov) if using_phantom_edge: ig.add_edge(phantom_edge) ig.t = s ig.make_minimal_isomorph() for j in range(len(flags)): if ig.is_labelled_isomorphic(flags[j]): row[j] += factor total += factor break for j in range(len(flags)): row[j] /= total rows.append(row) return matrix_of_independent_rows(self._field, rows, len(flags))
def cardinality(self): r""" Return the cardinality of ``self``. The number of ordered set partitions of a set of length `k` with composition shape `\mu` is equal to .. MATH:: \frac{k!}{\prod_{\mu_i \neq 0} \mu_i!}. EXAMPLES:: sage: OrderedSetPartitions(5,[2,3]).cardinality() 10 sage: OrderedSetPartitions(0, []).cardinality() 1 sage: OrderedSetPartitions(0, [0]).cardinality() 1 sage: OrderedSetPartitions(0, [0,0]).cardinality() 1 sage: OrderedSetPartitions(5, [2,0,3]).cardinality() 10 """ return factorial(len(self._set))/prod([factorial(i) for i in self.c])
def cardinality(self): r""" Return the cardinality of ``self``. The number of ordered set partitions of a set of length `k` with composition shape `\mu` is equal to .. MATH:: \frac{k!}{\prod_{\mu_i \neq 0} \mu_i!}. EXAMPLES:: sage: OrderedSetPartitions(5,[2,3]).cardinality() 10 sage: OrderedSetPartitions(0, []).cardinality() 1 sage: OrderedSetPartitions(0, [0]).cardinality() 1 sage: OrderedSetPartitions(0, [0,0]).cardinality() 1 sage: OrderedSetPartitions(5, [2,0,3]).cardinality() 10 """ return factorial(len(self._set)) / prod([factorial(i) for i in self.c])
def cardinality(self): """ Returns the number of Lyndon words with the evaluation e. EXAMPLES:: sage: LyndonWords([]).cardinality() 0 sage: LyndonWords([2,2]).cardinality() 1 sage: LyndonWords([2,3,2]).cardinality() 30 Check to make sure that the count matches up with the number of Lyndon words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2]] + Compositions(4).list() sage: lws = [LyndonWords(comp) for comp in comps] sage: all(lw.cardinality() == len(lw.list()) for lw in lws) True """ evaluation = self._e le = builtins.list(evaluation) if len(evaluation) == 0: return 0 n = sum(evaluation) return sum([moebius(j)*factorial(n/j) / prod([factorial(ni/j) for ni in evaluation]) for j in divisors(gcd(le))])/n
def by_taylor_expansion(self, fs, k, is_integral=False): r""" We combine the theta decomposition and the heat operator as in [Sko]. This yields a bijections of Jacobi forms of weight `k` and `M_k \times S_{k+2} \times .. \times S_{k+2m}`. """ ## we introduce an abbreviations if is_integral: PS = self.integral_power_series_ring() else: PS = self.power_series_ring() if not len(fs) == self.__precision.jacobi_index() + 1: raise ValueError( "fs must be a list of m + 1 elliptic modular forms or their fourier expansion" ) qexp_prec = self._qexp_precision() if qexp_prec is None: # there are no forms below the precision return dict() f_divs = dict() for (i, f) in enumerate(fs): f_divs[(i, 0)] = PS(f(qexp_prec), qexp_prec) if self.__precision.jacobi_index() == 1: return self._by_taylor_expansion_m1(f_divs, k, is_integral) for i in xrange(self.__precision.jacobi_index() + 1): for j in xrange(1, self.__precision.jacobi_index() - i + 1): f_divs[(i, j)] = f_divs[(i, j - 1)].derivative().shift(1) phi_divs = list() for i in xrange(self.__precision.jacobi_index() + 1): ## This is the formula in Skoruppas thesis. He uses d/ d tau instead of d / dz which yields ## a factor 4 m phi_divs.append( sum(f_divs[(j, i - j)] * (4 * self.__precision.jacobi_index())**i * binomial(i, j) / 2**i #2**(self.__precision.jacobi_index() - i + 1) * prod(2 * (i - l) + 1 for l in xrange(1, i)) / factorial(i + k + j - 1) * factorial(2 * self.__precision.jacobi_index() + k - 1) for j in xrange(i + 1))) phi_coeffs = dict() for r in xrange(self.__precision.jacobi_index() + 1): series = sum(map(operator.mul, self._theta_factors()[r], phi_divs)) series = self._eta_factor() * series for n in xrange(qexp_prec): phi_coeffs[(n, r)] = series[n] return phi_coeffs
def volume_hamming(n,q,r): r""" Returns number of elements in a Hamming ball of radius r in `\GF{q}^n`. Agrees with Guava's SphereContent(n,r,GF(q)). EXAMPLES:: sage: codes.bounds.volume_hamming(10,2,3) 176 """ ans=sum([factorial(n)/(factorial(i)*factorial(n-i))*(q-1)**i for i in range(r+1)]) return ans
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 by_taylor_expansion(self, fs, k, is_integral=False) : r""" We combine the theta decomposition and the heat operator as in [Sko]. This yields a bijections of Jacobi forms of weight `k` and `M_k \times S_{k+2} \times .. \times S_{k+2m}`. """ ## we introduce an abbreviations if is_integral : PS = self.integral_power_series_ring() else : PS = self.power_series_ring() if not len(fs) == self.__precision.jacobi_index() + 1 : raise ValueError("fs must be a list of m + 1 elliptic modular forms or their fourier expansion") qexp_prec = self._qexp_precision() if qexp_prec is None : # there are no forms below the precision return dict() f_divs = dict() for (i, f) in enumerate(fs) : f_divs[(i, 0)] = PS(f(qexp_prec), qexp_prec) if self.__precision.jacobi_index() == 1 : return self._by_taylor_expansion_m1(f_divs, k, is_integral) for i in xrange(self.__precision.jacobi_index() + 1) : for j in xrange(1, self.__precision.jacobi_index() - i + 1) : f_divs[(i,j)] = f_divs[(i, j - 1)].derivative().shift(1) phi_divs = list() for i in xrange(self.__precision.jacobi_index() + 1) : ## This is the formula in Skoruppas thesis. He uses d/ d tau instead of d / dz which yields ## a factor 4 m phi_divs.append( sum( f_divs[(j, i - j)] * (4 * self.__precision.jacobi_index())**i * binomial(i,j) / 2**i#2**(self.__precision.jacobi_index() - i + 1) * prod(2*(i - l) + 1 for l in xrange(1, i)) / factorial(i + k + j - 1) * factorial(2*self.__precision.jacobi_index() + k - 1) for j in xrange(i + 1) ) ) phi_coeffs = dict() for r in xrange(self.__precision.jacobi_index() + 1) : series = sum( map(operator.mul, self._theta_factors()[r], phi_divs) ) series = self._eta_factor() * series for n in xrange(qexp_prec) : phi_coeffs[(n, r)] = series[n] return phi_coeffs
def volume_hamming(n,q,r): r""" Returns the number of elements in a Hamming ball. Returns the number of elements in a Hamming ball of radius `r` in `\GF{q}^n`. EXAMPLES:: sage: codes.bounds.volume_hamming(10,2,3) 176 """ ans=sum([factorial(n)/(factorial(i)*factorial(n-i))*(q-1)**i for i in range(r+1)]) return ans
def volume_hamming(n, q, r): r""" Returns number of elements in a Hamming ball of radius r in `\GF{q}^n`. Agrees with Guava's SphereContent(n,r,GF(q)). EXAMPLES:: sage: codes.bounds.volume_hamming(10,2,3) 176 """ ans = sum([ factorial(n) / (factorial(i) * factorial(n - i)) * (q - 1)**i for i in range(r + 1) ]) return ans
def subgraph_densities(self, n): if n < 0: raise ValueError if self._use_symmetry: return self.symm_subgraph_densities(n) cn = self._graph.n total = Integer(0) sharp_graph_counts = {} sharp_graphs = [] for P in UnorderedTuples(range(1, cn + 1), n): factor = factorial(n) for i in range(1, cn + 1): factor /= factorial(P.count(i)) if self._weights: for v in P: factor *= self._weights[v - 1] ig = self._graph.degenerate_induced_subgraph(P) igc = copy(ig) # copy for phantom edge ig.make_minimal_isomorph() ghash = hash(ig) if ghash in sharp_graph_counts: sharp_graph_counts[ghash] += factor else: sharp_graphs.append(ig) sharp_graph_counts[ghash] = factor total += factor if hasattr(self, "_phantom_edge") and all( x in P for x in self._phantom_edge): phantom_edge = [P.index(x) + 1 for x in self._phantom_edge] igc.add_edge(phantom_edge) igc.make_minimal_isomorph() ghash = hash(igc) if not ghash in sharp_graph_counts: sharp_graphs.append(igc) sharp_graph_counts[ghash] = Integer(0) return [(g, sharp_graph_counts[hash(g)] / total) for g in sharp_graphs]
def f(partition): n = 0 m = 1 for part in partition: n += part m *= factorial(part) return t**n/m
def d(p): r""" Difference between the number of odd spin parity and even spin parity standard labeled permutations with given profile There is an explicit formula .. MATH:: d(p) = \frac{(n-1)!}{2^{(n-k)/2}} where `n` is the sum of the partition `p` and `k` is its length. EXAMPLES:: sage: import surface_dynamics.interval_exchanges.rauzy_class_cardinality as rcc sage: p = [3,3,1] sage: rcc.d([3,3,1]) == factorial(6) / 2**2 True sage: rcc.d([13]) == factorial(12) / 2**6 True Adding marked points:: sage: p = [5,3,3] sage: n = sum(p) sage: all(rcc.d(p + [1]*k) == factorial(n+k-1) / factorial(n-1) * rcc.d(p) for k in xrange(1,6)) True """ n = sum(p) k = len(p) return factorial(n - 1) / 2**((n - k) / 2)
def d(p): r""" Difference between the number of odd spin parity and even spin parity standard labeled permutations with given profile There is an explicit formula .. MATH:: d(p) = \frac{(n-1)!}{2^{(n-k)/2}} where `n` is the sum of the partition `p` and `k` is its length. EXAMPLES:: sage: import surface_dynamics.interval_exchanges.rauzy_class_cardinality as rcc sage: p = [3,3,1] sage: rcc.d([3,3,1]) == factorial(6) / 2**2 True sage: rcc.d([13]) == factorial(12) / 2**6 True Adding marked points:: sage: p = [5,3,3] sage: n = sum(p) sage: all(rcc.d(p + [1]*k) == factorial(n+k-1) / factorial(n-1) * rcc.d(p) for k in xrange(1,6)) True """ n = sum(p) k = len(p) return factorial(n-1) / 2**((n-k)/2)
def subgraph_densities(self, n): if n < 0: raise ValueError if self._use_symmetry: return self.symm_subgraph_densities(n) cn = self._graph.n total = Integer(0) sharp_graph_counts = {} sharp_graphs = [] for P in UnorderedTuples(range(1, cn + 1), n): factor = factorial(n) for i in range(1, cn + 1): factor /= factorial(P.count(i)) if self._weights: for v in P: factor *= self._weights[v - 1] ig = self._graph.degenerate_induced_subgraph(P) igc = copy(ig) # copy for phantom edge ig.make_minimal_isomorph() ghash = hash(ig) if ghash in sharp_graph_counts: sharp_graph_counts[ghash] += factor else: sharp_graphs.append(ig) sharp_graph_counts[ghash] = factor total += factor if hasattr(self, "_phantom_edge") and all(x in P for x in self._phantom_edge): phantom_edge = [P.index(x) + 1 for x in self._phantom_edge] igc.add_edge(phantom_edge) igc.make_minimal_isomorph() ghash = hash(igc) if not ghash in sharp_graph_counts: sharp_graphs.append(igc) sharp_graph_counts[ghash] = Integer(0) return [(g, sharp_graph_counts[hash(g)] / total) for g in sharp_graphs]
def quadratic_L_function__exact(n, d): r""" Returns the exact value of a quadratic twist of the Riemann Zeta function by `\chi_d(x) = \left(\frac{d}{x}\right)`. The input `n` must be a critical value. EXAMPLES:: sage: quadratic_L_function__exact(1, -4) 1/4*pi sage: quadratic_L_function__exact(-4, -4) 5/2 sage: quadratic_L_function__exact(2, 1) 1/6*pi^2 TESTS:: sage: quadratic_L_function__exact(2, -4) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. odd > 0 or even <= 0) REFERENCES: - [Iwa1972]_, pp 16-17, Special values of `L(1-n, \chi)` and `L(n, \chi)` - [IR1990]_ - [Was1997]_ """ from sage.all import SR, sqrt if n <= 0: return QuadraticBernoulliNumber(1 - n, d) / (n - 1) elif n >= 1: # Compute the kind of critical values (p10) if kronecker_symbol(fundamental_discriminant(d), -1) == 1: delta = 0 else: delta = 1 # Compute the positive special values (p17) if ((n - delta) % 2 == 0): f = abs(fundamental_discriminant(d)) if delta == 0: GS = sqrt(f) else: GS = I * sqrt(f) ans = SR(ZZ(-1)**(1 + (n - delta) / 2)) ans *= (2 * pi / f)**n ans *= GS # Evaluate the Gauss sum here! =0 ans *= QQ.one() / (2 * I**delta) ans *= QuadraticBernoulliNumber(n, d) / factorial(n) return ans else: if delta == 0: raise TypeError( "n must be a critical value (i.e. even > 0 or odd < 0)") if delta == 1: raise TypeError( "n must be a critical value (i.e. odd > 0 or even <= 0)")
def _c_rec(p): r""" Recurrence function that is called by :func:`c`. """ if p[0] == 1: return factorial(len(p) - 1) return (sum(_c_rec(split(p, k)) for k in xrange(1, p[0] - 1)) + sum(p[i] * _c_rec(collapse(p, 0, i)) for i in xrange(1, len(p))))
def cardinality(self): r""" Return the number of integer necklaces with the evaluation ``content``. The formula for the number of necklaces of content `\alpha` a composition of `n` is: .. MATH:: \sum_{d|gcd(\alpha)} \phi(d) \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d}, where `\phi(d)` is the Euler `\phi` function. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 sage: Necklaces([0,3,2]).cardinality() 2 Check to make sure that the count matches up with the number of necklace words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list() sage: ns = [ Necklaces(comp) for comp in comps] sage: all( [ n.cardinality() == len(n.list()) for n in ns] ) True """ evaluation = self._content le = list(evaluation) if not le: return 0 n = sum(le) return sum( euler_phi(j) * factorial(n / j) / prod(factorial(ni / j) for ni in evaluation) for j in divisors(gcd(le))) / n
def cardinality(self): r""" Return the number of integer necklaces with the evaluation ``content``. The formula for the number of necklaces of content `\alpha` a composition of `n` is: .. MATH:: \sum_{d|gcd(\alpha)} \phi(d) \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d}, where `\phi(d)` is the Euler `\phi` function. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 sage: Necklaces([0,3,2]).cardinality() 2 Check to make sure that the count matches up with the number of necklace words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list() sage: ns = [Necklaces(comp) for comp in comps] sage: all(n.cardinality() == len(n.list()) for n in ns) True """ evaluation = self._content le = list(evaluation) if not le: return ZZ.zero() n = sum(le) return ZZ.sum(euler_phi(j) * factorial(n // j) // prod(factorial(ni // j) for ni in evaluation) for j in divisors(gcd(le))) // n
def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function The argument must be a critical value, namely either positive even or negative odd. See for example [Iwasawa]_, p13, Special value of `\zeta(2k)` EXAMPLES: Let us test the accuracy for negative special values:: sage: RR = RealField(100) sage: for i in range(1,10): ... print "zeta(" + str(1-2*i) + "): ", RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 Let us test the accuracy for positive special values:: sage: all(abs(RR(zeta__exact(2*i))-zeta(RR(2*i))) < 10**(-28) for i in range(1,10)) True TESTS:: sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: .. [Iwasawa] Iwasawa, *Lectures on p-adic L-functions* .. [IreRos] Ireland and Rosen, *A Classical Introduction to Modern Number Theory* .. [WashCyc] Washington, *Cyclotomic Fields* """ if n < 0: return bernoulli(1 - n) / (n - 1) elif n > 1: if (n % 2 == 0): return ZZ(-1)**(n / 2 + 1) * ZZ(2)**( n - 1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError( "n must be a critical value (i.e. even > 0 or odd < 0)") elif n == 1: return infinity elif n == 0: return -1 / 2
def quadratic_L_function__exact(n, d): r""" Returns the exact value of a quadratic twist of the Riemann Zeta function by `\chi_d(x) = \left(\frac{d}{x}\right)`. The input `n` must be a critical value. EXAMPLES:: sage: quadratic_L_function__exact(1, -4) 1/4*pi sage: quadratic_L_function__exact(-4, -4) 5/2 sage: quadratic_L_function__exact(2, 1) 1/6*pi^2 TESTS:: sage: quadratic_L_function__exact(2, -4) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. odd > 0 or even <= 0) REFERENCES: - [Iwa1972]_, pp 16-17, Special values of `L(1-n, \chi)` and `L(n, \chi)` - [IR1990]_ - [Was1997]_ """ from sage.all import SR, sqrt if n <= 0: return QuadraticBernoulliNumber(1-n,d)/(n-1) elif n >= 1: # Compute the kind of critical values (p10) if kronecker_symbol(fundamental_discriminant(d), -1) == 1: delta = 0 else: delta = 1 # Compute the positive special values (p17) if ((n - delta) % 2 == 0): f = abs(fundamental_discriminant(d)) if delta == 0: GS = sqrt(f) else: GS = I * sqrt(f) ans = SR(ZZ(-1)**(1+(n-delta)/2)) ans *= (2*pi/f)**n ans *= GS # Evaluate the Gauss sum here! =0 ans *= QQ.one()/(2 * I**delta) ans *= QuadraticBernoulliNumber(n,d)/factorial(n) return ans else: if delta == 0: raise TypeError("n must be a critical value (i.e. even > 0 or odd < 0)") if delta == 1: raise TypeError("n must be a critical value (i.e. odd > 0 or even <= 0)")
def _c_rec(p): r""" Recurrence function that is called by :func:`c`. """ if p[0] == 1: return factorial(len(p)-1) return ( sum(_c_rec(split(p,k)) for k in xrange(1,p[0]-1)) + sum(p[i]*_c_rec(collapse(p,0,i)) for i in xrange(1,len(p))))
def cardinality(self): r""" Return the cardinality of ``self``. The number of fully packed loops on `n \times n` grid .. MATH:: \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!}. EXAMPLES:: sage: [AlternatingSignMatrices(n).cardinality() for n in range(0, 11)] [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] """ return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) for k in range(self._n)] ))
def cardinality(self): r""" Return the cardinality of ``self``. The number of decorated permutations of size `n` is equal to .. MATH:: \sum_{k=0^n} \frac{n!}{k!} EXAMPLES:: sage: [DecoratedPermutations(n).cardinality() for n in range(11)] [1, 2, 5, 16, 65, 326, 1957, 13700, 109601, 986410, 9864101] """ return Integer( sum( factorial(self._n) // factorial(k) for k in range(self._n + 1)))
def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function The argument must be a critical value, namely either positive even or negative odd. See for example [Iwasawa]_, p13, Special value of `\zeta(2k)` EXAMPLES: Let us test the accuracy for negative special values:: sage: RR = RealField(100) sage: for i in range(1,10): ... print "zeta(" + str(1-2*i) + "): ", RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 Let us test the accuracy for positive special values:: sage: all(abs(RR(zeta__exact(2*i))-zeta(RR(2*i))) < 10**(-28) for i in range(1,10)) True TESTS:: sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: .. [Iwasawa] Iwasawa, *Lectures on p-adic L-functions* .. [IreRos] Ireland and Rosen, *A Classical Introduction to Modern Number Theory* .. [WashCyc] Washington, *Cyclotomic Fields* """ if n < 0: return bernoulli(1-n)/(n-1) elif n > 1: if (n % 2 == 0): return ZZ(-1)**(n/2 + 1) * ZZ(2)**(n-1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError("n must be a critical value (i.e. even > 0 or odd < 0)") elif n==1: return infinity elif n==0: return -1/2
def cardinality(self): """ Return the cardinality of ``self``. The number of ordered partitions of a set of size `n` into `k` parts is equal to `k! S(n,k)` where `S(n,k)` denotes the Stirling number of the second kind. EXAMPLES:: sage: OrderedSetPartitions(4,2).cardinality() 14 sage: OrderedSetPartitions(4,1).cardinality() 1 """ return factorial(self.n) * stirling_number2(len(self._set), self.n)
def cardinality(self): """ Return the cardinality of ``self``. The number of ordered partitions of a set of size `n` into `k` parts is equal to `k! S(n,k)` where `S(n,k)` denotes the Stirling number of the second kind. EXAMPLES:: sage: OrderedSetPartitions(4,2).cardinality() 14 sage: OrderedSetPartitions(4,1).cardinality() 1 """ return factorial(self.n)*stirling_number2(len(self._set), self.n)
def cardinality(self): """ EXAMPLES:: sage: OrderedSetPartitions(0).cardinality() 1 sage: OrderedSetPartitions(1).cardinality() 1 sage: OrderedSetPartitions(2).cardinality() 3 sage: OrderedSetPartitions(3).cardinality() 13 sage: OrderedSetPartitions([1,2,3]).cardinality() 13 sage: OrderedSetPartitions(4).cardinality() 75 sage: OrderedSetPartitions(5).cardinality() 541 """ return sum([factorial(k)*stirling_number2(len(self._set),k) for k in range(len(self._set)+1)])
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.arith.all import factorial if not w: 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 tuple_orbit_reps(self, k, prefix=None): if prefix is None: prefix = [] s = len(prefix) tp = tuple(prefix) if s > k: raise ValueError if k == 0: return 1, {(): 1} gens = self._graph.automorphism_group_gens() # Pass generators to GAP to create a group for us. gen_str = ",".join("(" + "".join(str(cy) for cy in cys) + ")" for cys in gens) gap.eval("g := Group(%s);" % gen_str) if len(prefix) > 0: gap.eval("g := Stabilizer(g, %s, OnTuples);" % list(set(prefix))) S = [] for i in range(1, k - s + 1): S.extend( [tuple(sorted(list(x))) for x in Subsets(self._graph.n, i)]) set_orb_reps = {} #sys.stdout.write("Calculating orbits") while len(S) > 0: rep = list(S[0]) o = gap.new("Orbit(g, %s, OnSets);" % (rep, )).sage() o = list(set([tuple(sorted(t)) for t in o])) ot = o[0] set_orb_reps[ot] = len(o) for t in o: S.remove(t) #sys.stdout.write(".") #sys.stdout.flush() #sys.stdout.write("\n") combs = [tuple(c) for c in Compositions(k - s)] factors = [] for c in combs: factor = factorial(k - s) for x in c: factor /= factorial(x) factors.append(factor) orb_reps = {} total = 0 for ot, length in set_orb_reps.iteritems(): ne = len(ot) for ci in range(len(combs)): c = combs[ci] if len(c) == ne: t = tp for i in range(ne): t += c[i] * (ot[i], ) weight = factors[ci] * length orb_reps[t] = weight total += weight return total, orb_reps
def gamma__exact(n): r""" Evaluates the exact value of the `\Gamma` function at an integer or half-integer argument. EXAMPLES:: sage: gamma__exact(4) 6 sage: gamma__exact(3) 2 sage: gamma__exact(2) 1 sage: gamma__exact(1) 1 sage: gamma__exact(1/2) sqrt(pi) sage: gamma__exact(3/2) 1/2*sqrt(pi) sage: gamma__exact(5/2) 3/4*sqrt(pi) sage: gamma__exact(7/2) 15/8*sqrt(pi) sage: gamma__exact(-1/2) -2*sqrt(pi) sage: gamma__exact(-3/2) 4/3*sqrt(pi) sage: gamma__exact(-5/2) -8/15*sqrt(pi) sage: gamma__exact(-7/2) 16/105*sqrt(pi) TESTS:: sage: gamma__exact(1/3) Traceback (most recent call last): ... TypeError: you must give an integer or half-integer argument """ from sage.all import sqrt n = QQ(n) if denominator(n) == 1: if n <= 0: return infinity if n > 0: return factorial(n - 1) elif denominator(n) == 2: ans = QQ.one() while n != QQ((1, 2)): if n < 0: ans /= n n += 1 elif n > 0: n += -1 ans *= n ans *= sqrt(pi) return ans else: raise TypeError("you must give an integer or half-integer argument")
def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): """ Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print(e.degree_divisor) ....: print(e.degree_multiple) 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import verbose, cputime degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen().__pari__() verbose("Starting field: %s"%Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree//absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s"%[s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd(factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac//2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq//2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q/q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([4*a0*a2 - a1*a1 -a0*a3*a3, a1*a3 - 4*a0, -a2, 1]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq//3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s"%[s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]"%(degree_divisor,degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError("inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError("inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s"%(f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol*(denom**Mpol.poldegree())).subst("x", pari([0,1/denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s"%Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0])-1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod(Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL/Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s"%Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm//f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k*KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd(f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol/Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
def _closed_form(hyp): a, b, z = hyp.operands() a, b = a.operands(), b.operands() p, q = len(a), len(b) if z == 0: return Integer(1) if p == q == 0: return exp(z) if p == 1 and q == 0: return (1 - z) ** (-a[0]) if p == 0 and q == 1: # TODO: make this require only linear time def _0f1(b, z): F12 = cosh(2 * sqrt(z)) F32 = sinh(2 * sqrt(z)) / (2 * sqrt(z)) if 2 * b == 1: return F12 if 2 * b == 3: return F32 if 2 * b > 3: return ((b - 2) * (b - 1) / z * (_0f1(b - 2, z) - _0f1(b - 1, z))) if 2 * b < 1: return (_0f1(b + 1, z) + z / (b * (b + 1)) * _0f1(b + 2, z)) raise ValueError # Can evaluate 0F1 in terms of elementary functions when # the parameter is a half-integer if 2 * b[0] in ZZ and b[0] not in ZZ: return _0f1(b[0], z) # Confluent hypergeometric function if p == 1 and q == 1: aa, bb = a[0], b[0] if aa * 2 == 1 and bb * 2 == 3: t = sqrt(-z) return sqrt(pi) / 2 * erf(t) / t if a == 1 and b == 2: return (exp(z) - 1) / z n, m = aa, bb if n in ZZ and m in ZZ and m > 0 and n > 0: rf = rising_factorial if m <= n: return (exp(z) * sum(rf(m - n, k) * (-z) ** k / factorial(k) / rf(m, k) for k in xrange(n - m + 1))) else: T = sum(rf(n - m + 1, k) * z ** k / (factorial(k) * rf(2 - m, k)) for k in xrange(m - n)) U = sum(rf(1 - n, k) * (-z) ** k / (factorial(k) * rf(2 - m, k)) for k in xrange(n)) return (factorial(m - 2) * rf(1 - m, n) * z ** (1 - m) / factorial(n - 1) * (T - exp(z) * U)) if p == 2 and q == 1: R12 = QQ('1/2') R32 = QQ('3/2') def _2f1(a, b, c, z): """ Evaluation of 2F1(a, b; c; z), assuming a, b, c positive integers or half-integers """ if b == c: return (1 - z) ** (-a) if a == c: return (1 - z) ** (-b) if a == 0 or b == 0: return Integer(1) if a > b: a, b = b, a if b >= 2: F1 = _2f1(a, b - 1, c, z) F2 = _2f1(a, b - 2, c, z) q = (b - 1) * (z - 1) return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + (b - c - 1) * F2) / q) if c > 2: # how to handle this case? if a - c + 1 == 0 or b - c + 1 == 0: raise NotImplementedError F1 = _2f1(a, b, c - 1, z) F2 = _2f1(a, b, c - 2, z) r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) r2 = (c - 1) * (c - 2) * (1 - z) q = (a - c + 1) * (b - c + 1) * z return (r1 * F1 + r2 * F2) / q if (a, b, c) == (R12, 1, 2): return (2 - 2 * sqrt(1 - z)) / z if (a, b, c) == (1, 1, 2): return -log(1 - z) / z if (a, b, c) == (1, R32, R12): return (1 + z) / (1 - z) ** 2 if (a, b, c) == (1, R32, 2): return 2 * (1 / sqrt(1 - z) - 1) / z if (a, b, c) == (R32, 2, R12): return (1 + 3 * z) / (1 - z) ** 3 if (a, b, c) == (R32, 2, 1): return (2 + z) / (2 * (sqrt(1 - z) * (1 - z) ** 2)) if (a, b, c) == (2, 2, 1): return (1 + z) / (1 - z) ** 3 raise NotImplementedError aa, bb = a cc, = b if z == 1: return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) / gamma(cc - bb)) if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and aa > 0 and bb > 0 and cc > 0): try: return _2f1(aa, bb, cc, z) except NotImplementedError: pass return hyp
def tuple_orbit_reps(self, k, prefix=None): if prefix is None: prefix = [] s = len(prefix) tp = tuple(prefix) if s > k: raise ValueError if k == 0: return 1, {() : 1} gens = self._graph.automorphism_group_gens() # Pass generators to GAP to create a group for us. gen_str = ",".join("(" + "".join(str(cy) for cy in cys) + ")" for cys in gens) gap.eval("g := Group(%s);" % gen_str) if len(prefix) > 0: gap.eval("g := Stabilizer(g, %s, OnTuples);" % list(set(prefix))) S = [] for i in range(1, k - s + 1): S.extend([tuple(sorted(list(x))) for x in Subsets(self._graph.n, i)]) set_orb_reps = {} #sys.stdout.write("Calculating orbits") while len(S) > 0: rep = list(S[0]) o = gap.new("Orbit(g, %s, OnSets);" % (rep,)).sage() o = list(set([tuple(sorted(t)) for t in o])) ot = o[0] set_orb_reps[ot] = len(o) for t in o: S.remove(t) #sys.stdout.write(".") #sys.stdout.flush() #sys.stdout.write("\n") combs = [tuple(c) for c in Compositions(k - s)] factors = [] for c in combs: factor = factorial(k - s) for x in c: factor /= factorial(x) factors.append(factor) orb_reps = {} total = 0 for ot, length in set_orb_reps.iteritems(): ne = len(ot) for ci in range(len(combs)): c = combs[ci] if len(c) == ne: t = tp for i in range(ne): t += c[i] * (ot[i],) weight = factors[ci] * length orb_reps[t] = weight total += weight return total, orb_reps
def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): r""" Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: from sage.misc.verbose import set_verbose sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print(e.degree_divisor) ....: print(e.degree_multiple) 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import cputime from sage.misc.verbose import verbose degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen().__pari__() verbose("Starting field: %s" % Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree // absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s" % [s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd( factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac // 2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq // 2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q / q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([ 4 * a0 * a2 - a1 * a1 - a0 * a3 * a3, a1 * a3 - 4 * a0, -a2, 1 ]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq // 3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort( absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s" % [s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]" % (degree_divisor, degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s" % (f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol * (denom**Mpol.poldegree())).subst( "x", pari([0, 1 / denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s" % Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0]) - 1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod( Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL / Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s" % Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm // f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k * KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd( f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol / Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
def K1_func(SUK, v, A, prec=106): r""" Return the constant `K_1` from Smart's TCDF paper, [Sma1995]_ INPUT: - ``SUK`` -- a group of `S`-units - ``v`` -- an infinite place of ``K`` (element of ``SUK.number_field().places(prec)``) - ``A`` -- a list of all products of each potential ``a``, ``b`` in the $S$-unit equation ``ax + by + 1 = 0`` with each root of unity of ``K`` - ``prec`` -- (default: 106) the precision of the real field OUTPUT: The constant ``K1,`` a real number EXAMPLES:: sage: from sage.rings.number_field.S_unit_solver import K1_func sage: K.<xi> = NumberField(x^3-3) sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3))) sage: phi_real = K.places()[0] sage: phi_complex = K.places()[1] sage: A = K.roots_of_unity() sage: K1_func(SUK, phi_real, A) 4.396386097852707394927181864635e16 sage: K1_func(SUK, phi_complex, A) 2.034870098399844430207420286581e17 REFERENCES: - [Sma1995]_ p. 825 """ R = RealField(prec) #[Sma1995]_ p. 825 if is_real_place(v): c11 = R(4*c4_func(SUK, v, A, prec)).log() / c3_func(SUK, prec) else: c11 = 2*( R(4*(c4_func(SUK,v, A, prec)).sqrt()).log() ) / c3_func(SUK, prec) #[Sma1995]_ p. 825 if is_real_place(v): c12 = R(2 * c4_func(SUK, v, A, prec)) else: c12 = R(2 * c4_func(SUK, v, A, prec).sqrt()) #[Sma1998]_ p. 225, Theorem A.1 d = SUK.number_field().degree() t = SUK.rank() Baker_C = R(18 * factorial(t+2) * (t+1)**(t+2) * (32*d)**(t+3) * R(2*(t+1) * d).log()) def hprime(SUK, alpha, v): #[Sma1998]_ p. 225 return R(max(alpha.global_height(), 1/SUK.number_field().degree(), v(alpha).log().abs()/SUK.number_field().degree())) #[Sma1995]_ p. 825 and [Sma1998]_ p. 225, Theorem A.1 c14 = Baker_C * prod([hprime(SUK, alpha, v) for alpha in SUK.gens_values()]) #[Sma1995]_ p. 825 c15 = 2 * (c12.log()+c14*((SUK.rank()+1)*c14/c13_func(SUK, v, prec)).log()) / c13_func(SUK, v, prec) return max([c11, c15])
def _closed_form(hyp): a, b, z = hyp.operands() a, b = a.operands(), b.operands() p, q = len(a), len(b) if z == 0: return Integer(1) if p == q == 0: return exp(z) if p == 1 and q == 0: return (1 - z)**(-a[0]) if p == 0 and q == 1: # TODO: make this require only linear time def _0f1(b, z): F12 = cosh(2 * sqrt(z)) F32 = sinh(2 * sqrt(z)) / (2 * sqrt(z)) if 2 * b == 1: return F12 if 2 * b == 3: return F32 if 2 * b > 3: return ((b - 2) * (b - 1) / z * (_0f1(b - 2, z) - _0f1(b - 1, z))) if 2 * b < 1: return (_0f1(b + 1, z) + z / (b * (b + 1)) * _0f1(b + 2, z)) raise ValueError # Can evaluate 0F1 in terms of elementary functions when # the parameter is a half-integer if 2 * b[0] in ZZ and b[0] not in ZZ: return _0f1(b[0], z) # Confluent hypergeometric function if p == 1 and q == 1: aa, bb = a[0], b[0] if aa * 2 == 1 and bb * 2 == 3: t = sqrt(-z) return sqrt(pi) / 2 * erf(t) / t if a == 1 and b == 2: return (exp(z) - 1) / z n, m = aa, bb if n in ZZ and m in ZZ and m > 0 and n > 0: rf = rising_factorial if m <= n: return (exp(z) * sum( rf(m - n, k) * (-z)**k / factorial(k) / rf(m, k) for k in range(n - m + 1))) else: T = sum( rf(n - m + 1, k) * z**k / (factorial(k) * rf(2 - m, k)) for k in range(m - n)) U = sum( rf(1 - n, k) * (-z)**k / (factorial(k) * rf(2 - m, k)) for k in range(n)) return (factorial(m - 2) * rf(1 - m, n) * z**(1 - m) / factorial(n - 1) * (T - exp(z) * U)) if p == 2 and q == 1: R12 = QQ((1, 2)) R32 = QQ((3, 2)) def _2f1(a, b, c, z): """ Evaluation of 2F1(a, b; c; z), assuming a, b, c positive integers or half-integers """ if b == c: return (1 - z)**(-a) if a == c: return (1 - z)**(-b) if a == 0 or b == 0: return Integer(1) if a > b: a, b = b, a if b >= 2: F1 = _2f1(a, b - 1, c, z) F2 = _2f1(a, b - 2, c, z) q = (b - 1) * (z - 1) return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + (b - c - 1) * F2) / q) if c > 2: # how to handle this case? if a - c + 1 == 0 or b - c + 1 == 0: raise NotImplementedError F1 = _2f1(a, b, c - 1, z) F2 = _2f1(a, b, c - 2, z) r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) r2 = (c - 1) * (c - 2) * (1 - z) q = (a - c + 1) * (b - c + 1) * z return (r1 * F1 + r2 * F2) / q if (a, b, c) == (R12, 1, 2): return (2 - 2 * sqrt(1 - z)) / z if (a, b, c) == (1, 1, 2): return -log(1 - z) / z if (a, b, c) == (1, R32, R12): return (1 + z) / (1 - z)**2 if (a, b, c) == (1, R32, 2): return 2 * (1 / sqrt(1 - z) - 1) / z if (a, b, c) == (R32, 2, R12): return (1 + 3 * z) / (1 - z)**3 if (a, b, c) == (R32, 2, 1): return (2 + z) / (2 * (sqrt(1 - z) * (1 - z)**2)) if (a, b, c) == (2, 2, 1): return (1 + z) / (1 - z)**3 raise NotImplementedError aa, bb = a cc, = b if z == 1: return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) / gamma(cc - bb)) if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and aa > 0 and bb > 0 and cc > 0): try: return _2f1(aa, bb, cc, z) except NotImplementedError: pass return hyp
def gamma__exact(n): """ Evaluates the exact value of the `\Gamma` function at an integer or half-integer argument. EXAMPLES:: sage: gamma__exact(4) 6 sage: gamma__exact(3) 2 sage: gamma__exact(2) 1 sage: gamma__exact(1) 1 sage: gamma__exact(1/2) sqrt(pi) sage: gamma__exact(3/2) 1/2*sqrt(pi) sage: gamma__exact(5/2) 3/4*sqrt(pi) sage: gamma__exact(7/2) 15/8*sqrt(pi) sage: gamma__exact(-1/2) -2*sqrt(pi) sage: gamma__exact(-3/2) 4/3*sqrt(pi) sage: gamma__exact(-5/2) -8/15*sqrt(pi) sage: gamma__exact(-7/2) 16/105*sqrt(pi) TESTS:: sage: gamma__exact(1/3) Traceback (most recent call last): ... TypeError: you must give an integer or half-integer argument """ from sage.all import sqrt n = QQ(n) if denominator(n) == 1: if n <= 0: return infinity if n > 0: return factorial(n-1) elif denominator(n) == 2: ans = QQ.one() while n != QQ((1, 2)): if n < 0: ans /= n n += 1 elif n > 0: n += -1 ans *= n ans *= sqrt(pi) return ans else: raise TypeError("you must give an integer or half-integer argument")
def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function The argument must be a critical value, namely either positive even or negative odd. See for example [Iwa1972]_, p13, Special value of `\zeta(2k)` EXAMPLES: Let us test the accuracy for negative special values:: sage: RR = RealField(100) sage: for i in range(1,10): ....: print("zeta({}): {}".format(1-2*i, RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)))) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 Let us test the accuracy for positive special values:: sage: all(abs(RR(zeta__exact(2*i))-zeta(RR(2*i))) < 10**(-28) for i in range(1,10)) True TESTS:: sage: zeta__exact(4) 1/90*pi^4 sage: zeta__exact(-3) 1/120 sage: zeta__exact(0) -1/2 sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: - [Iwa1972]_ - [IR1990]_ - [Was1997]_ """ if n < 0: return bernoulli(1-n)/(n-1) elif n > 1: if (n % 2 == 0): return ZZ(-1)**(n//2 + 1) * ZZ(2)**(n-1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError("n must be a critical value (i.e. even > 0 or odd < 0)") elif n == 1: return infinity elif n == 0: return QQ((-1, 2))
def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function The argument must be a critical value, namely either positive even or negative odd. See for example [Iwa1972]_, p13, Special value of `\zeta(2k)` EXAMPLES: Let us test the accuracy for negative special values:: sage: RR = RealField(100) sage: for i in range(1,10): ....: print("zeta({}): {}".format(1-2*i, RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)))) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 Let us test the accuracy for positive special values:: sage: all(abs(RR(zeta__exact(2*i))-zeta(RR(2*i))) < 10**(-28) for i in range(1,10)) True TESTS:: sage: zeta__exact(4) 1/90*pi^4 sage: zeta__exact(-3) 1/120 sage: zeta__exact(0) -1/2 sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: - [Iwa1972]_ - [IR1990]_ - [Was1997]_ """ if n < 0: return bernoulli(1 - n) / (n - 1) elif n > 1: if (n % 2 == 0): return ZZ(-1)**(n // 2 + 1) * ZZ(2)**( n - 1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError( "n must be a critical value (i.e. even > 0 or odd < 0)") elif n == 1: return infinity elif n == 0: return QQ((-1, 2))
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