Example #1
0
    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])
Example #4
0
    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])
Example #5
0
    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))
Example #7
0
    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
Example #8
0
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
Example #9
0
    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
Example #11
0
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
Example #12
0
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]
Example #14
0
 def f(partition):
     n = 0
     m = 1
     for part in partition:
         n += part
         m *= factorial(part)
     return t**n/m
Example #15
0
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]
Example #18
0
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)")
Example #19
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))))
Example #20
0
    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
Example #21
0
    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
Example #22
0
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
Example #23
0
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))))
Example #25
0
    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)] ))
Example #26
0
    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)))
Example #27
0
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
Example #28
0
    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 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)])
Example #32
0
    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
Example #34
0
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")
Example #35
0
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
Example #36
0
    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
Example #38
0
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
Example #39
0
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])
Example #40
0
    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
Example #41
0
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")
Example #42
0
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))
Example #43
0
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))
Example #44
0
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