Ask Your Question

Revision history [back]

Hi, for many computations in algebraic number theory SAGE uses PARI in the background. It seems to me that the relevant functionality for computing ray class groups (that is present in PARI) is not yet wrapped conveniently in SAGE.

There are two ways to proceed: You could use PARI/GP directly from the command line (the functions you're looking for are basically bnrinit to compute a representation of a ray class group as a product of cyclic groups and bnrisprincipal to compute the image of an ideal in this representation, from which you can get the order). Alternatively, you can use SAGE's GP-Interface to do the same from within SAGE, I'll demonstrate both ways.

First, an example in PARI/GP to demonstrate the basic computation (some output omitted, $\mathfrak m = 3$ and $K$ has no real places):

? K = bnfinit(x^6 + 76*x^4 + 2*x^3 + 2029*x^2 - 158*x + 18955)
? bnfcertify(K)
%2 = 1
? Kr = bnrinit(K, [3, []], 1)
? Kr.clgp.cyc
%4 = [6, 6]     # => ray class group is C_6 x C_6
? J = idealhnf(Kr, 5, x^2 + 2*x + 23)
? bnrisprincipal(Kr,J)
%6 = [[4, 4]~, ...]  # image of J in ray class group

The following provides a quick and dirty hack to do basically the same from SAGE, using its PARI/GP Interface:

class RayClassGroup(AbelianGroup_class):
    def __init__(self, number_field, mod_ideal = 1, mod_archimedean = None):
        if mod_archimedean == None:
            mod_archimedean = [0] * len(number_field.real_places())

        bnf = gp(number_field.pari_bnf())
        # Use PARI to compute ray class group
        bnr = bnf.bnrinit([mod_ideal, mod_archimedean],1)
        invariants = bnr[5][2]         # bnr.clgp.cyc
        invariants = [ ZZ(x) for x in invariants ] 

        AbelianGroup_class.__init__(self, len(invariants), invariants)
        self.__number_field = number_field
        self.__bnr = bnr

    def __call__(self, *args, **kwargs):
        return group.Group.__call__(self, *args, **kwargs)

    def _element_constructor_(self, *args, **kwargs):
        if isinstance(args[0], AbelianGroupElement):
            return AbelianGroupElement(self, args[0])
        else:
            I = self.__number_field.ideal(*args, **kwargs)

            # Use PARI to compute class of given ideal
            g = self.__bnr.bnrisprincipal(I)[1]
            g = [ ZZ(x) for x in g ]
            return AbelianGroupElement(self, g)

After running the above code (either in a notebook cell or the sage command-line, be careful to preserve the whitespace exactly as is), you can use the class like this:

K.<a> = NumberField(x^6 + 76*x^4 + 2*x^3 + 2029*x^2 - 158*x + 18955)
G = RayClassGroup(K, K.ideal(3), [])
J = K.ideal([5, a^2 + 2*a + 23])
G(J).order()

If you have real places, the infinite part is specified using 0's and 1's, e.g.

K.<a> = NumberField(x^6 - 3*x^5 - 3*x^4 + 10*x^3 + 3*x^2 - 6*x + 1)
G = RayClassGroup(K, K.ideal(1), [0,1,1,1,1,1])

gives trivial ray class group, while

K.<a> = NumberField(x^6 - 3*x^5 - 3*x^4 + 10*x^3 + 3*x^2 - 6*x + 1)
G = RayClassGroup(K, K.ideal(1), [1,1,1,1,1,1])

gives $C_2$. The order of places in this vector corresponds to the order of real roots of K.defining_polynomial().

Hi, for many computations in algebraic number theory SAGE uses PARI in the background. It seems to me that the relevant functionality for computing ray class groups (that is present in PARI) is not yet wrapped conveniently in SAGE.

There are two ways to proceed: You could use PARI/GP directly from the command line (the functions you're looking for are basically bnrinit to compute a representation of a ray class group as a product of cyclic groups and bnrisprincipal to compute the image of an ideal in this representation, from which you can get the order). Alternatively, you can use SAGE's GP-Interface to do the same from within SAGE, I'll demonstrate both ways.

First, an example in PARI/GP to demonstrate the basic computation (some output omitted, $\mathfrak m = 3$ (3)$ and $K$ has no real archimedean places):

? K = bnfinit(x^6 + 76*x^4 + 2*x^3 + 2029*x^2 - 158*x + 18955)
? bnfcertify(K)
%2 = 1
? Kr = bnrinit(K, [3, []], 1)
? Kr.clgp.cyc
%4 = [6, 6]     # => ray class group is C_6 x C_6
? J = idealhnf(Kr, 5, x^2 + 2*x + 23)
? bnrisprincipal(Kr,J)
%6 = [[4, 4]~, ...]  # image of J in ray class group

The following provides a quick and dirty hack to do basically the same from SAGE, using its PARI/GP Interface:

class RayClassGroup(AbelianGroup_class):
    def __init__(self, number_field, mod_ideal = 1, mod_archimedean = None):
        if mod_archimedean == None:
            mod_archimedean = [0] * len(number_field.real_places())

        bnf = gp(number_field.pari_bnf())
        # Use PARI to compute ray class group
        bnr = bnf.bnrinit([mod_ideal, mod_archimedean],1)
        invariants = bnr[5][2]         # bnr.clgp.cyc
        invariants = [ ZZ(x) for x in invariants ] 

        AbelianGroup_class.__init__(self, len(invariants), invariants)
        self.__number_field = number_field
        self.__bnr = bnr

    def __call__(self, *args, **kwargs):
        return group.Group.__call__(self, *args, **kwargs)

    def _element_constructor_(self, *args, **kwargs):
        if isinstance(args[0], AbelianGroupElement):
            return AbelianGroupElement(self, args[0])
        else:
            I = self.__number_field.ideal(*args, **kwargs)

            # Use PARI to compute class of given ideal
            g = self.__bnr.bnrisprincipal(I)[1]
            g = [ ZZ(x) for x in g ]
            return AbelianGroupElement(self, g)

After running the above code (either in a notebook cell or the sage command-line, be careful to preserve the whitespace exactly as is), you can use the class like this:

K.<a> = NumberField(x^6 + 76*x^4 + 2*x^3 + 2029*x^2 - 158*x + 18955)
G = RayClassGroup(K, K.ideal(3), [])
J = K.ideal([5, a^2 + 2*a + 23])
G(J).order()

If you have real places, the infinite part is specified using 0's and 1's, e.g.

K.<a> = NumberField(x^6 - 3*x^5 - 3*x^4 + 10*x^3 + 3*x^2 - 6*x + 1)
G = RayClassGroup(K, K.ideal(1), [0,1,1,1,1,1])

gives trivial ray class group, while

K.<a> = NumberField(x^6 - 3*x^5 - 3*x^4 + 10*x^3 + 3*x^2 - 6*x + 1)
G = RayClassGroup(K, K.ideal(1), [1,1,1,1,1,1])

gives $C_2$. The order of places in this vector corresponds to the order of real roots of K.defining_polynomial().