def __init__(self, n): """ Initialize ``self``. EXAMPLES:: sage: G = groups.matrix.BinaryDihedral(4) sage: TestSuite(G).run() """ self._n = n if n % 2 == 0: R = CyclotomicField(2 * n) zeta = R.gen() i = R.gen()**(n // 2) else: R = CyclotomicField(4 * n) zeta = R.gen()**2 i = R.gen()**n MS = MatrixSpace(R, 2) zero = R.zero() gens = [MS([zeta, zero, zero, ~zeta]), MS([zero, i, i, zero])] from sage.libs.gap.libgap import libgap gap_gens = [libgap(matrix_gen) for matrix_gen in gens] gap_group = libgap.Group(gap_gens) FinitelyGeneratedMatrixGroup_gap.__init__(self, ZZ(2), R, gap_group, category=Groups().Finite())
def __init__(self, n): """ Initialize ``self``. EXAMPLES:: sage: G = groups.matrix.BinaryDihedral(4) sage: TestSuite(G).run() """ self._n = n if n % 2 == 0: R = CyclotomicField(2*n) zeta = R.gen() i = R.gen()**(n//2) else: R = CyclotomicField(4*n) zeta = R.gen()**2 i = R.gen()**n MS = MatrixSpace(R, 2) zero = R.zero() gens = [ MS([zeta, zero, zero, ~zeta]), MS([zero, i, i, zero]) ] from sage.libs.gap.libgap import libgap gap_gens = [libgap(matrix_gen) for matrix_gen in gens] gap_group = libgap.Group(gap_gens) FinitelyGeneratedMatrixGroup_gap.__init__(self, ZZ(2), R, gap_group, category=Groups().Finite())
def to_matrix(self): """ Return a matrix of ``self``. The colors are mapped to roots of unity. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: s1,s2,t = C.gens() sage: x = s1*s2*t*s2; x.one_line_form() [(1, 2), (0, 1), (0, 3)] sage: M = x.to_matrix(); M [ 0 1 0] [zeta4 0 0] [ 0 0 1] The matrix multiplication is in the *opposite* order:: sage: M == s2.to_matrix()*t.to_matrix()*s2.to_matrix()*s1.to_matrix() True """ Cp = CyclotomicField(self.parent()._m) g = Cp.gen() D = diagonal_matrix(Cp, [g**i for i in self._colors]) return self._perm.to_matrix() * D
def to_matrix(self): """ Return a matrix of ``self``. The colors are mapped to roots of unity. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: s1,s2,t = C.gens() sage: x = s1*s2*t*s2; x.one_line_form() [(1, 2), (0, 1), (0, 3)] sage: M = x.to_matrix(); M [ 0 1 0] [zeta4 0 0] [ 0 0 1] The matrix multiplication is in the *opposite* order:: sage: M == s2.to_matrix()*t.to_matrix()*s2.to_matrix()*s1.to_matrix() True """ Cp = CyclotomicField(self.parent()._m) g = Cp.gen() D = diagonal_matrix(Cp, [g ** i for i in self._colors]) return self._perm.to_matrix() * D
def _calcMatrixTrans(calc, tS, tT, lS, lT): """ See `calcMatrixTrans()` for the main documentation. This is the lower-level function which is wrapped through `persistent_cache`. This makes the cache more flexible about different parameters `R` to `calcMatrixTrans()`. """ try: ms = calc.calcMatrixTrans(tS, tT, lS, lT) except Exception: print (calc.params, calc.curlS, tS, tT, lS, lT) raise # Each matrix is for a zeta**i factor, where zeta is the n-th root of unity. # And n = calc.matrixCountTrans. assert len(ms) == calc.matrixCountTrans order = len(ms) K = CyclotomicField(order) zeta = K.gen() Kcoords = zeta.coordinates_in_terms_of_powers() assert len(K.power_basis()) == K.degree() new_ms = [matrix(QQ, ms[0].nrows(), ms[0].ncols()) for i in range(K.degree())] for l in range(order): coords = Kcoords(zeta**l) for i,m in enumerate(coords): new_ms[i] += ms[l] * m ms = new_ms denom = calc.matrixRowDenomTrans denom, ms = reduceNRow(denom=denom, mats=ms) return denom, order, ms
def to_cyclotomic_field(self, R=None): r""" Return this element as an element of a cyclotomic field. EXAMPLES:: sage: UCF = UniversalCyclotomicField() sage: UCF.gen(3).to_cyclotomic_field() zeta3 sage: UCF.gen(3,2).to_cyclotomic_field() -zeta3 - 1 sage: CF = CyclotomicField(5) sage: CF(E(5)) # indirect doctest zeta5 sage: CF = CyclotomicField(7) sage: CF(E(5)) # indirect doctest Traceback (most recent call last): ... TypeError: Cannot coerce zeta5 into Cyclotomic Field of order 7 and degree 6 sage: CF = CyclotomicField(10) sage: CF(E(5)) # indirect doctest zeta10^2 Matrices are correctly dealt with:: sage: M = Matrix(UCF,2,[E(3),E(4),E(5),E(6)]); M [ E(3) E(4)] [ E(5) -E(3)^2] sage: Matrix(CyclotomicField(60),M) # indirect doctest [zeta60^10 - 1 zeta60^15] [ zeta60^12 zeta60^10] Using a non-standard embedding:: sage: CF = CyclotomicField(5,embedding=CC(exp(4*pi*i/5))) sage: x = E(5) sage: CC(x) 0.309016994374947 + 0.951056516295154*I sage: CC(CF(x)) 0.309016994374947 + 0.951056516295154*I """ from sage.rings.number_field.number_field import CyclotomicField k = self._obj.Conductor().sage() Rcan = CyclotomicField(k) if R is None: R = Rcan obj = self._obj if obj.IsRat(): return R(obj.sage()) zeta = Rcan.gen() coeffs = obj.CoeffsCyc(k).sage() return R(sum(coeffs[a] * zeta**a for a in range(1,k)))
def to_cyclotomic_field(self, R=None): r""" Return this element as an element of a cyclotomic field. EXAMPLES:: sage: UCF = UniversalCyclotomicField() sage: UCF.gen(3).to_cyclotomic_field() zeta3 sage: UCF.gen(3,2).to_cyclotomic_field() -zeta3 - 1 sage: CF = CyclotomicField(5) sage: CF(E(5)) # indirect doctest zeta5 sage: CF = CyclotomicField(7) sage: CF(E(5)) # indirect doctest Traceback (most recent call last): ... TypeError: Cannot coerce zeta5 into Cyclotomic Field of order 7 and degree 6 sage: CF = CyclotomicField(10) sage: CF(E(5)) # indirect doctest zeta10^2 Matrices are correctly dealt with:: sage: M = Matrix(UCF,2,[E(3),E(4),E(5),E(6)]); M [ E(3) E(4)] [ E(5) -E(3)^2] sage: Matrix(CyclotomicField(60),M) # indirect doctest [zeta60^10 - 1 zeta60^15] [ zeta60^12 zeta60^10] Using a non-standard embedding:: sage: CF = CyclotomicField(5,embedding=CC(exp(4*pi*i/5))) sage: x = E(5) sage: CC(x) 0.309016994374947 + 0.951056516295154*I sage: CC(CF(x)) 0.309016994374947 + 0.951056516295154*I """ from sage.rings.number_field.number_field import CyclotomicField k = self._obj.Conductor().sage() Rcan = CyclotomicField(k) if R is None: R = Rcan obj = self._obj if obj.IsRat(): return R(obj.sage()) zeta = Rcan.gen() coeffs = obj.CoeffsCyc(k).sage() return R(sum(coeffs[a] * zeta**a for a in range(1, k)))
def _rank(self, K) : if K is QQ or K in NumberFields() : return len(_jacobi_forms_by_taylor_expansion_coords(self.__index, self.__weight, 0)) ## This is the formula used by Poor and Yuen in Paramodular cusp forms if self.__weight == 2 : delta = len(self.__index.divisors()) // 2 - 1 else : delta = 0 return sum( ModularForms(1, self.__weight + 2 * j).dimension() + j**2 // (4 * self.__index) for j in xrange(self.__index + 1) ) \ + delta ## This is the formula given by Skoruppa in ## Jacobi forms of critical weight and Weil representations ##FIXME: There is some mistake here if self.__weight % 2 != 0 : ## Otherwise the space X(i**(n - 2 k)) is different ## See: Skoruppa, Jacobi forms of critical weight and Weil representations raise NotImplementedError m = self.__index K = CyclotomicField(24 * m, 'zeta') zeta = K.gen(0) quadform = lambda x : 6 * x**2 bilinform = lambda x,y : quadform(x + y) - quadform(x) - quadform(y) T = diagonal_matrix([zeta**quadform(i) for i in xrange(2*m)]) S = sum(zeta**(-quadform(x)) for x in xrange(2 * m)) / (2 * m) \ * matrix([[zeta**(-bilinform(j,i)) for j in xrange(2*m)] for i in xrange(2*m)]) subspace_matrix_1 = matrix( [ [1 if j == i or j == 2*m - i else 0 for j in xrange(m + 1) ] for i in xrange(2*m)] ) subspace_matrix_2 = zero_matrix(ZZ, m + 1, 2*m) subspace_matrix_2.set_block(0,0,identity_matrix(m+1)) T = subspace_matrix_2 * T * subspace_matrix_1 S = subspace_matrix_2 * S * subspace_matrix_1 sqrt3 = (zeta**(4*m) - zeta**(-4*m)) * zeta**(-6*m) rank = (self.__weight - 1/2 - 1) / 2 * (m + 1) \ + 1/8 * ( zeta**(3*m * (2*self.__weight - 1)) * S.trace() + zeta**(3*m * (1 - 2*self.__weight)) * S.trace().conjugate() ) \ + 2/(3*sqrt3) * ( zeta**(4 * m * self.__weight) * (S*T).trace() + zeta**(-4 * m * self.__weight) * (S*T).trace().conjugate() ) \ - sum((j**2 % (m+1))/(m+1) -1/2 for j in range(0,m+1)) if self.__weight > 5 / 2 : return rank else : raise NotImplementedError raise NotImplementedError
def sage_character_to_magma(chi,N=None,magma=None): if magma is None: magma = sage.interfaces.magma if N is None: N = chi.modulus() else: N = ZZ(N) chi = chi.extend(N) G = chi.parent() order = chi.order() gens = G.unit_gens() ElGm = magma.DirichletGroupFull(N) Kcyc = CyclotomicField(ElGm.BaseRing().CyclotomicOrder(),names='z') phi = ElGm.BaseRing().sage().hom([Kcyc.gen()]) target = [chi(g) for g in gens] for chim in ElGm.Elements(): if chim.Order().sage() == order: this = [phi(chim.Evaluate(g).sage()) for g in gens] if all((u == v for u, v in zip(this, target))): return chim raise RuntimeError("Should not get to this point")
def toCyclPowerBase(M, order): """ Let's K = CyclotomicField(order). INPUT: - `M` -- A matrix over the cyclomotic field `K`. - `order` -- The order of `K`, the cyclomotic field. OUTPUT: - A list of matrices `ms` in power base where every matrix is a factor to `zeta**i` where `zeta = K.gen()` and `len(ms) == K.degree()`. """ K = CyclotomicField(order) zeta = K.gen() Kcoords = zeta.coordinates_in_terms_of_powers() assert len(K.power_basis()) == K.degree() ms = [matrix(QQ,M.nrows(),M.ncols()) for i in range(K.degree())] for y in range(M.nrows()): for x in range(M.ncols()): try: v_ = M[y,x] v = K(v_) coords = Kcoords(v) except TypeError: print "type of {1} ({2}) is not valid in Cyclomotic field of order {0}".format(order, M[y,x], type(M[y,x])) raise assert len(coords) == K.degree() for i in range(K.degree()): ms[i][y,x] = coords[i] return ms
def molien_series(self, chi=None, return_series=True, prec=20, variable='t'): r""" Compute the Molien series of this finite group with respect to the character ``chi``. It can be returned either as a rational function in one variable or a power series in one variable. The base field must be a finite field, the rationals, or a cyclotomic field. Note that the base field characteristic cannot divide the group order (i.e., the non-modular case). ALGORITHM: For a finite group `G` in characteristic zero we construct the Molien series as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\text{det}(I-tg)}, where `I` is the identity matrix and `t` an indeterminate. For characteristic `p` not dividing the order of `G`, let `k` be the base field and `N` the order of `G`. Define `\lambda` as a primitive `N`-th root of unity over `k` and `\omega` as a primitive `N`-th root of unity over `\QQ`. For each `g \in G` define `k_i(g)` to be the positive integer such that `e_i = \lambda^{k_i(g)}` for each eigenvalue `e_i` of `g`. Then the Molien series is computed as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\prod_{i=1}^n(1 - t\omega^{k_i(g)})}, where `t` is an indeterminant. [Dec1998]_ INPUT: - ``chi`` -- (default: trivial character) a linear group character of this group - ``return_series`` -- boolean (default: ``True``) if ``True``, then returns the Molien series as a power series, ``False`` as a rational function - ``prec`` -- integer (default: 20); power series default precision - ``variable`` -- string (default: ``'t'``); Variable name for the Molien series OUTPUT: single variable rational function or power series with integer coefficients EXAMPLES:: sage: MatrixGroup(matrix(QQ,2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: only implemented for finite groups sage: MatrixGroup(matrix(GF(3),2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: characteristic cannot divide group order Tetrahedral Group:: sage: K.<i> = CyclotomicField(4) sage: Tetra = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0]) sage: Tetra.molien_series(prec=30) 1 + t^8 + 2*t^12 + t^16 + 2*t^20 + 3*t^24 + 2*t^28 + O(t^30) sage: mol = Tetra.molien_series(return_series=False); mol (t^8 - t^4 + 1)/(t^16 - t^12 - t^4 + 1) sage: mol.parent() Fraction Field of Univariate Polynomial Ring in t over Integer Ring sage: chi = Tetra.character(Tetra.character_table()[1]) sage: Tetra.molien_series(chi, prec=30, variable='u') u^6 + u^14 + 2*u^18 + u^22 + 2*u^26 + 3*u^30 + 2*u^34 + O(u^36) sage: chi = Tetra.character(Tetra.character_table()[2]) sage: Tetra.molien_series(chi) t^10 + t^14 + t^18 + 2*t^22 + 2*t^26 + O(t^30) :: sage: S3 = MatrixGroup(SymmetricGroup(3)) sage: mol = S3.molien_series(prec=10); mol 1 + t + 2*t^2 + 3*t^3 + 4*t^4 + 5*t^5 + 7*t^6 + 8*t^7 + 10*t^8 + 12*t^9 + O(t^10) sage: mol.parent() Power Series Ring in t over Integer Ring Octahedral Group:: sage: K.<v> = CyclotomicField(8) sage: a = v-v^3 #sqrt(2) sage: i = v^2 sage: Octa = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [(1+i)/a,0, 0,(1-i)/a]) sage: Octa.molien_series(prec=30) 1 + t^8 + t^12 + t^16 + t^18 + t^20 + 2*t^24 + t^26 + t^28 + O(t^30) Icosahedral Group:: sage: K.<v> = CyclotomicField(10) sage: z5 = v^2 sage: i = z5^5 sage: a = 2*z5^3 + 2*z5^2 + 1 #sqrt(5) sage: Ico = MatrixGroup([[z5^3,0, 0,z5^2], [0,1, -1,0], [(z5^4-z5)/a, (z5^2-z5^3)/a, (z5^2-z5^3)/a, -(z5^4-z5)/a]]) sage: Ico.molien_series(prec=40) 1 + t^12 + t^20 + t^24 + t^30 + t^32 + t^36 + O(t^40) :: sage: G = MatrixGroup(CyclicPermutationGroup(3)) sage: chi = G.character(G.character_table()[1]) sage: G.molien_series(chi, prec=10) t + 2*t^2 + 3*t^3 + 5*t^4 + 7*t^5 + 9*t^6 + 12*t^7 + 15*t^8 + 18*t^9 + 22*t^10 + O(t^11) :: sage: K = GF(5) sage: S = MatrixGroup(SymmetricGroup(4)) sage: G = MatrixGroup([matrix(K,4,4,[K(y) for u in m.list() for y in u])for m in S.gens()]) sage: G.molien_series(return_series=False) 1/(t^10 - t^9 - t^8 + 2*t^5 - t^2 - t + 1) :: sage: i = GF(7)(3) sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]]) sage: chi = G.character(G.character_table()[4]) sage: G.molien_series(chi) 3*t^5 + 6*t^11 + 9*t^17 + 12*t^23 + O(t^25) """ if not self.is_finite(): raise NotImplementedError("only implemented for finite groups") if chi is None: chi = self.trivial_character() M = self.matrix_space() R = FractionField(self.base_ring()) N = self.order() if R.characteristic() == 0: P = PolynomialRing(R, variable) t = P.gen() #it is possible the character is over a larger cyclotomic field K = chi.values()[0].parent() if K.degree() != 1: if R.degree() != 1: L = K.composite_fields(R)[0] else: L = K else: L = R mol = P(0) for g in self: mol += L(chi(g)) / (M.identity_matrix()-t*g.matrix()).det().change_ring(L) elif R.characteristic().divides(N): raise NotImplementedError("characteristic cannot divide group order") else: #char p>0 #find primitive Nth roots of unity over base ring and QQ F = cyclotomic_polynomial(N).change_ring(R) w = F.roots(ring=R.algebraic_closure(), multiplicities=False)[0] #don't need to extend further in this case since the order of #the roots of unity in the character divide the order of the group L = CyclotomicField(N, 'v') v = L.gen() #construct Molien series P = PolynomialRing(L, variable) t = P.gen() mol = P(0) for g in self: #construct Phi phi = L(chi(g)) for e in g.matrix().eigenvalues(): #find power such that w**n = e n = 1 while w**n != e and n < N+1: n += 1 #raise v to that power phi *= (1-t*v**n) mol += P(1)/phi #We know the coefficients will be integers mol = mol.numerator().change_ring(ZZ) / mol.denominator().change_ring(ZZ) #divide by group order mol /= N if return_series: PS = PowerSeriesRing(ZZ, variable, default_prec=prec) return PS(mol) return mol
def molien_series(self, chi=None, return_series=True, prec=20, variable='t'): r""" Compute the Molien series of this finite group with respect to the character ``chi``. It can be returned either as a rational function in one variable or a power series in one variable. The base field must be a finite field, the rationals, or a cyclotomic field. Note that the base field characteristic cannot divide the group order (i.e., the non-modular case). ALGORITHM: For a finite group `G` in characteristic zero we construct the Molien series as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\text{det}(I-tg)}, where `I` is the identity matrix and `t` an indeterminate. For characteristic `p` not dividing the order of `G`, let `k` be the base field and `N` the order of `G`. Define `\lambda` as a primitive `N`-th root of unity over `k` and `\omega` as a primitive `N`-th root of unity over `\QQ`. For each `g \in G` define `k_i(g)` to be the positive integer such that `e_i = \lambda^{k_i(g)}` for each eigenvalue `e_i` of `g`. Then the Molien series is computed as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\prod_{i=1}^n(1 - t\omega^{k_i(g)})}, where `t` is an indeterminant. [Dec1998]_ INPUT: - ``chi`` -- (default: trivial character) a linear group character of this group - ``return_series`` -- boolean (default: ``True``) if ``True``, then returns the Molien series as a power series, ``False`` as a rational function - ``prec`` -- integer (default: 20); power series default precision - ``variable`` -- string (default: ``'t'``); Variable name for the Molien series OUTPUT: single variable rational function or power series with integer coefficients EXAMPLES:: sage: MatrixGroup(matrix(QQ,2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: only implemented for finite groups sage: MatrixGroup(matrix(GF(3),2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: characteristic cannot divide group order Tetrahedral Group:: sage: K.<i> = CyclotomicField(4) sage: Tetra = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0]) sage: Tetra.molien_series(prec=30) 1 + t^8 + 2*t^12 + t^16 + 2*t^20 + 3*t^24 + 2*t^28 + O(t^30) sage: mol = Tetra.molien_series(return_series=False); mol (t^8 - t^4 + 1)/(t^16 - t^12 - t^4 + 1) sage: mol.parent() Fraction Field of Univariate Polynomial Ring in t over Integer Ring sage: chi = Tetra.character(Tetra.character_table()[1]) sage: Tetra.molien_series(chi, prec=30, variable='u') u^6 + u^14 + 2*u^18 + u^22 + 2*u^26 + 3*u^30 + 2*u^34 + O(u^36) sage: chi = Tetra.character(Tetra.character_table()[2]) sage: Tetra.molien_series(chi) t^10 + t^14 + t^18 + 2*t^22 + 2*t^26 + O(t^30) :: sage: S3 = MatrixGroup(SymmetricGroup(3)) sage: mol = S3.molien_series(prec=10); mol 1 + t + 2*t^2 + 3*t^3 + 4*t^4 + 5*t^5 + 7*t^6 + 8*t^7 + 10*t^8 + 12*t^9 + O(t^10) sage: mol.parent() Power Series Ring in t over Integer Ring Octahedral Group:: sage: K.<v> = CyclotomicField(8) sage: a = v-v^3 #sqrt(2) sage: i = v^2 sage: Octa = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [(1+i)/a,0, 0,(1-i)/a]) sage: Octa.molien_series(prec=30) 1 + t^8 + t^12 + t^16 + t^18 + t^20 + 2*t^24 + t^26 + t^28 + O(t^30) Icosahedral Group:: sage: K.<v> = CyclotomicField(10) sage: z5 = v^2 sage: i = z5^5 sage: a = 2*z5^3 + 2*z5^2 + 1 #sqrt(5) sage: Ico = MatrixGroup([[z5^3,0, 0,z5^2], [0,1, -1,0], [(z5^4-z5)/a, (z5^2-z5^3)/a, (z5^2-z5^3)/a, -(z5^4-z5)/a]]) sage: Ico.molien_series(prec=40) 1 + t^12 + t^20 + t^24 + t^30 + t^32 + t^36 + O(t^40) :: sage: G = MatrixGroup(CyclicPermutationGroup(3)) sage: chi = G.character(G.character_table()[1]) sage: G.molien_series(chi, prec=10) t + 2*t^2 + 3*t^3 + 5*t^4 + 7*t^5 + 9*t^6 + 12*t^7 + 15*t^8 + 18*t^9 + 22*t^10 + O(t^11) :: sage: K = GF(5) sage: S = MatrixGroup(SymmetricGroup(4)) sage: G = MatrixGroup([matrix(K,4,4,[K(y) for u in m.list() for y in u])for m in S.gens()]) sage: G.molien_series(return_series=False) 1/(t^10 - t^9 - t^8 + 2*t^5 - t^2 - t + 1) :: sage: i = GF(7)(3) sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]]) sage: chi = G.character(G.character_table()[4]) sage: G.molien_series(chi) 3*t^5 + 6*t^11 + 9*t^17 + 12*t^23 + O(t^25) """ if not self.is_finite(): raise NotImplementedError("only implemented for finite groups") if chi is None: chi = self.trivial_character() M = self.matrix_space() R = FractionField(self.base_ring()) N = self.order() if R.characteristic() == 0: P = PolynomialRing(R, variable) t = P.gen() #it is possible the character is over a larger cyclotomic field K = chi.values()[0].parent() if K.degree() != 1: if R.degree() != 1: L = K.composite_fields(R)[0] else: L = K else: L = R mol = P(0) for g in self: mol += L(chi(g)) / (M.identity_matrix() - t * g.matrix()).det().change_ring(L) elif R.characteristic().divides(N): raise NotImplementedError( "characteristic cannot divide group order") else: #char p>0 #find primitive Nth roots of unity over base ring and QQ F = cyclotomic_polynomial(N).change_ring(R) w = F.roots(ring=R.algebraic_closure(), multiplicities=False)[0] #don't need to extend further in this case since the order of #the roots of unity in the character divide the order of the group L = CyclotomicField(N, 'v') v = L.gen() #construct Molien series P = PolynomialRing(L, variable) t = P.gen() mol = P(0) for g in self: #construct Phi phi = L(chi(g)) for e in g.matrix().eigenvalues(): #find power such that w**n = e n = 1 while w**n != e and n < N + 1: n += 1 #raise v to that power phi *= (1 - t * v**n) mol += P(1) / phi #We know the coefficients will be integers mol = mol.numerator().change_ring(ZZ) / mol.denominator().change_ring( ZZ) #divide by group order mol /= N if return_series: PS = PowerSeriesRing(ZZ, variable, default_prec=prec) return PS(mol) return mol
def attack(m, q, r=4, sigma=3.0, subfield_only=False): K = CyclotomicField(m, 'z') z = K.gen() OK = K.ring_of_integers() G = K.galois_group() n = euler_phi(m) mprime = m / r nprime = euler_phi(mprime) Gprime = [tau for tau in G if tau(z**r) == z**r] R = PolynomialRing(IntegerRing(), 'a') a = R.gen() phim = a**n + 1 D = DiscreteGaussianDistributionIntegerSampler(sigma) print "sampling f,g" while True: f = sum([D() * z**i for i in range(n)]) fx = sum([f[i] * a**i for i in range(n)]) res = inverse(fx, phim, q) if res[0]: f_inv = sum([res[1][i] * z**i for i in range(n)]) print "f_inv * f = %s (mod %d)" % ((f * f_inv).mod(q), q) break g = sum([D() * z**i for i in range(n)]) print "done sampling f, g" #h = [g*f^{-1)]_q h = (g * f_inv).mod(q) lognorm_f = log(f.vector().norm(), 2) lognorm_g = log(g.vector().norm(), 2) print "f*h - g = %s" % (f * h - g).mod(q) print "log q = ", log(q, 2).n(precision) print "log |f| = %s, log |g| = %s" % (lognorm_f.n(precision), lognorm_g.n(precision)) print "log |(f,g)| = ", log( sqrt(f.vector().norm()**2 + g.vector().norm()**2), 2).n(precision) print "begin computing N(f), N(g), N(h), Tr(h), fbar" fprime = norm(f, Gprime) gprime = norm(g, Gprime) hprime = norm(h, Gprime).mod(q) htr = trace(h, Gprime) fbar = prod([tau(f) for tau in Gprime[1:]]) print "end computing N(f), N(g), N(h), Tr(h), fbar" lognorm_fp = log(fprime.vector().norm(), 2) lognorm_gp = log(gprime.vector().norm(), 2) print "%d * log |f| - log |f'| = %s" % (r, r * lognorm_f.n(precision) - lognorm_fp.n(precision)) print "log |(f', g')| = ", log( sqrt(fprime.vector().norm()**2 + gprime.vector().norm()**2), 2).n(precision) print "log |N(f), Tr(g fbar)| = ", log( sqrt(fprime.vector().norm()**2 + trace(g * fbar, Gprime).vector().norm()**2), 2).n(precision) #(fprime, gprime) lies in the lattice \Lambda_hprime^q print "f'*h' - g' = %s " % (hprime * fprime - gprime).mod(q) print "N(f) Tr(h) - Tr(g fbar) = %s" % (htr * fprime - trace(g * fbar, Gprime)).mod(q) if not subfield_only: ntru_full = NTRU(h, K, q) full_sv = ntru_full.shortest_vector() print "log |v| = %s" % log(full_sv.norm(), 2).n(precision) ntru_subfield = NTRU_subfield(hprime, q, nprime, r) ntru_trace_subfield = NTRU_subfield(htr, q, nprime, r) print "begin computing Shortest Vector of subfield lattice" norm_sv = ntru_subfield.shortest_vector() tr_sv = ntru_trace_subfield.shortest_vector() print "end computing Shortest Vector of subfield lattice" norm_xp = sum( [coerce(Integer, norm_sv[i]) * z**(r * i) for i in range(nprime)]) tr_xp = sum( [coerce(Integer, tr_sv[i]) * z**(r * i) for i in range(nprime)]) print "Norm map: log |(x',y')| = ", log(norm_sv.norm(), 2).n(precision) print "Trace map: log |(x', y')| = ", log(tr_sv.norm(), 2).n(precision) #test if xprime belongs to <fprime> mat = [] for i in range(nprime): coordinate = (fprime * z**(r * i)).vector().list() mat.append([coordinate[r * j] for j in range(nprime)]) FL = IntegerLattice(mat) print norm_sv[:nprime] in FL print tr_sv[:nprime] in FL norm_x = norm_xp norm_y = mod_q(norm_x * h, q) tr_x = tr_xp tr_y = mod_q(tr_x * h, q) print "Norm map: log |(x,y)| = ", log( sqrt(norm_x.vector().norm()**2 + norm_y.vector().norm()**2), 2).n(precision) print "Trace map: log |(x,y)| = ", log( sqrt(tr_x.vector().norm()**2 + tr_y.vector().norm()**2), 2).n(precision)
def Omega_ge(a, exponents): r""" Return `\Omega_{\ge}` of the expression specified by the input. To be more precise, calculate .. MATH:: \Omega_{\ge} \frac{\mu^a}{ (1 - z_0 \mu^{e_0}) \dots (1 - z_{n-1} \mu^{e_{n-1}})} and return its numerator and a factorization of its denominator. Note that `z_0`, ..., `z_{n-1}` only appear in the output, but not in the input. INPUT: - ``a`` -- an integer - ``exponents`` -- a tuple of integers OUTPUT: A pair representing a quotient as follows: Its first component is the numerator as a Laurent polynomial, its second component a factorization of the denominator as a tuple of Laurent polynomials, where each Laurent polynomial `z` represents a factor `1 - z`. The parents of these Laurent polynomials is always a Laurent polynomial ring in `z_0`, ..., `z_{n-1}` over `\ZZ`, where `n` is the length of ``exponents``. EXAMPLES:: sage: from sage.rings.polynomial.omega import Omega_ge sage: Omega_ge(0, (1, -2)) (1, (z0, z0^2*z1)) sage: Omega_ge(0, (1, -3)) (1, (z0, z0^3*z1)) sage: Omega_ge(0, (1, -4)) (1, (z0, z0^4*z1)) sage: Omega_ge(0, (2, -1)) (z0*z1 + 1, (z0, z0*z1^2)) sage: Omega_ge(0, (3, -1)) (z0*z1^2 + z0*z1 + 1, (z0, z0*z1^3)) sage: Omega_ge(0, (4, -1)) (z0*z1^3 + z0*z1^2 + z0*z1 + 1, (z0, z0*z1^4)) sage: Omega_ge(0, (1, 1, -2)) (-z0^2*z1*z2 - z0*z1^2*z2 + z0*z1*z2 + 1, (z0, z1, z0^2*z2, z1^2*z2)) sage: Omega_ge(0, (2, -1, -1)) (z0*z1*z2 + z0*z1 + z0*z2 + 1, (z0, z0*z1^2, z0*z2^2)) sage: Omega_ge(0, (2, 1, -1)) (-z0*z1*z2^2 - z0*z1*z2 + z0*z2 + 1, (z0, z1, z0*z2^2, z1*z2)) :: sage: Omega_ge(0, (2, -2)) (-z0*z1 + 1, (z0, z0*z1, z0*z1)) sage: Omega_ge(0, (2, -3)) (z0^2*z1 + 1, (z0, z0^3*z1^2)) sage: Omega_ge(0, (3, 1, -3)) (-z0^3*z1^3*z2^3 + 2*z0^2*z1^3*z2^2 - z0*z1^3*z2 + z0^2*z2^2 - 2*z0*z2 + 1, (z0, z1, z0*z2, z0*z2, z0*z2, z1^3*z2)) :: sage: Omega_ge(0, (3, 6, -1)) (-z0*z1*z2^8 - z0*z1*z2^7 - z0*z1*z2^6 - z0*z1*z2^5 - z0*z1*z2^4 + z1*z2^5 - z0*z1*z2^3 + z1*z2^4 - z0*z1*z2^2 + z1*z2^3 - z0*z1*z2 + z0*z2^2 + z1*z2^2 + z0*z2 + z1*z2 + 1, (z0, z1, z0*z2^3, z1*z2^6)) TESTS:: sage: Omega_ge(0, (2, 2, 1, 1, 1, -1, -1))[0].number_of_terms() # long time 1695 sage: Omega_ge(0, (2, 2, 1, 1, 1, 1, 1, -1, -1))[0].number_of_terms() # not tested (too long, 1 min) 27837 :: sage: Omega_ge(1, (2,)) (1, (z0,)) """ import logging logger = logging.getLogger(__name__) logger.info('Omega_ge: a=%s, exponents=%s', a, exponents) from sage.arith.all import lcm, srange from sage.rings.integer_ring import ZZ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.number_field.number_field import CyclotomicField if not exponents or any(e == 0 for e in exponents): raise NotImplementedError rou = sorted(set(abs(e) for e in exponents) - set([1])) ellcm = lcm(rou) B = CyclotomicField(ellcm, 'zeta') zeta = B.gen() z_names = tuple('z{}'.format(i) for i in range(len(exponents))) L = LaurentPolynomialRing(B, ('t', ) + z_names, len(z_names) + 1) t = L.gens()[0] Z = LaurentPolynomialRing(ZZ, z_names, len(z_names)) powers = {i: L(zeta**(ellcm // i)) for i in rou} powers[2] = L(-1) powers[1] = L(1) exponents_and_values = tuple( (e, tuple(powers[abs(e)]**j * z for j in srange(abs(e)))) for z, e in zip(L.gens()[1:], exponents)) x = tuple(v for e, v in exponents_and_values if e > 0) y = tuple(v for e, v in exponents_and_values if e < 0) def subs_power(expression, var, exponent): r""" Substitute ``var^exponent`` by ``var`` in ``expression``. It is assumed that ``var`` only occurs with exponents divisible by ``exponent``. """ p = tuple(var.dict().popitem()[0]).index( 1) # var is the p-th generator def subs_e(e): e = list(e) assert e[p] % exponent == 0 e[p] = e[p] // exponent return tuple(e) parent = expression.parent() result = parent( {subs_e(e): c for e, c in iteritems(expression.dict())}) return result def de_power(expression): expression = Z(expression) for e, var in zip(exponents, Z.gens()): if abs(e) == 1: continue expression = subs_power(expression, var, abs(e)) return expression logger.debug('Omega_ge: preparing denominator') factors_denominator = tuple( de_power(1 - factor) for factor in _Omega_factors_denominator_(x, y)) logger.debug('Omega_ge: preparing numerator') numerator = de_power(_Omega_numerator_(a, x, y, t)) logger.info('Omega_ge: completed') return numerator, factors_denominator
def Omega_ge(a, exponents): r""" Return `\Omega_{\ge}` of the expression specified by the input. To be more precise, calculate .. MATH:: \Omega_{\ge} \frac{\mu^a}{ (1 - z_0 \mu^{e_0}) \dots (1 - z_{n-1} \mu^{e_{n-1}})} and return its numerator and a factorization of its denominator. Note that `z_0`, ..., `z_{n-1}` only appear in the output, but not in the input. INPUT: - ``a`` -- an integer - ``exponents`` -- a tuple of integers OUTPUT: A pair representing a quotient as follows: Its first component is the numerator as a Laurent polynomial, its second component a factorization of the denominator as a tuple of Laurent polynomials, where each Laurent polynomial `z` represents a factor `1 - z`. The parents of these Laurent polynomials is always a Laurent polynomial ring in `z_0`, ..., `z_{n-1}` over `\ZZ`, where `n` is the length of ``exponents``. EXAMPLES:: sage: from sage.rings.polynomial.omega import Omega_ge sage: Omega_ge(0, (1, -2)) (1, (z0, z0^2*z1)) sage: Omega_ge(0, (1, -3)) (1, (z0, z0^3*z1)) sage: Omega_ge(0, (1, -4)) (1, (z0, z0^4*z1)) sage: Omega_ge(0, (2, -1)) (z0*z1 + 1, (z0, z0*z1^2)) sage: Omega_ge(0, (3, -1)) (z0*z1^2 + z0*z1 + 1, (z0, z0*z1^3)) sage: Omega_ge(0, (4, -1)) (z0*z1^3 + z0*z1^2 + z0*z1 + 1, (z0, z0*z1^4)) sage: Omega_ge(0, (1, 1, -2)) (-z0^2*z1*z2 - z0*z1^2*z2 + z0*z1*z2 + 1, (z0, z1, z0^2*z2, z1^2*z2)) sage: Omega_ge(0, (2, -1, -1)) (z0*z1*z2 + z0*z1 + z0*z2 + 1, (z0, z0*z1^2, z0*z2^2)) sage: Omega_ge(0, (2, 1, -1)) (-z0*z1*z2^2 - z0*z1*z2 + z0*z2 + 1, (z0, z1, z0*z2^2, z1*z2)) :: sage: Omega_ge(0, (2, -2)) (-z0*z1 + 1, (z0, z0*z1, z0*z1)) sage: Omega_ge(0, (2, -3)) (z0^2*z1 + 1, (z0, z0^3*z1^2)) sage: Omega_ge(0, (3, 1, -3)) (-z0^3*z1^3*z2^3 + 2*z0^2*z1^3*z2^2 - z0*z1^3*z2 + z0^2*z2^2 - 2*z0*z2 + 1, (z0, z1, z0*z2, z0*z2, z0*z2, z1^3*z2)) :: sage: Omega_ge(0, (3, 6, -1)) (-z0*z1*z2^8 - z0*z1*z2^7 - z0*z1*z2^6 - z0*z1*z2^5 - z0*z1*z2^4 + z1*z2^5 - z0*z1*z2^3 + z1*z2^4 - z0*z1*z2^2 + z1*z2^3 - z0*z1*z2 + z0*z2^2 + z1*z2^2 + z0*z2 + z1*z2 + 1, (z0, z1, z0*z2^3, z1*z2^6)) TESTS:: sage: Omega_ge(0, (2, 2, 1, 1, 1, 1, 1, -1, -1))[0].number_of_terms() # long time 27837 :: sage: Omega_ge(1, (2,)) (1, (z0,)) """ import logging logger = logging.getLogger(__name__) logger.info('Omega_ge: a=%s, exponents=%s', a, exponents) from sage.arith.all import lcm, srange from sage.rings.integer_ring import ZZ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.number_field.number_field import CyclotomicField if not exponents or any(e == 0 for e in exponents): raise NotImplementedError rou = sorted(set(abs(e) for e in exponents) - set([1])) ellcm = lcm(rou) B = CyclotomicField(ellcm, 'zeta') zeta = B.gen() z_names = tuple('z{}'.format(i) for i in range(len(exponents))) L = LaurentPolynomialRing(B, ('t',) + z_names, len(z_names) + 1) t = L.gens()[0] Z = LaurentPolynomialRing(ZZ, z_names, len(z_names)) powers = {i: L(zeta**(ellcm//i)) for i in rou} powers[2] = L(-1) powers[1] = L(1) exponents_and_values = tuple( (e, tuple(powers[abs(e)]**j * z for j in srange(abs(e)))) for z, e in zip(L.gens()[1:], exponents)) x = tuple(v for e, v in exponents_and_values if e > 0) y = tuple(v for e, v in exponents_and_values if e < 0) def subs_power(expression, var, exponent): r""" Substitute ``var^exponent`` by ``var`` in ``expression``. It is assumed that ``var`` only occurs with exponents divisible by ``exponent``. """ p = tuple(var.dict().popitem()[0]).index(1) # var is the p-th generator def subs_e(e): e = list(e) assert e[p] % exponent == 0 e[p] = e[p] // exponent return tuple(e) parent = expression.parent() result = parent({subs_e(e): c for e, c in iteritems(expression.dict())}) return result def de_power(expression): expression = Z(expression) for e, var in zip(exponents, Z.gens()): if abs(e) == 1: continue expression = subs_power(expression, var, abs(e)) return expression logger.debug('Omega_ge: preparing denominator') factors_denominator = tuple(de_power(1 - factor) for factor in _Omega_factors_denominator_(x, y)) logger.debug('Omega_ge: preparing numerator') numerator = de_power(_Omega_numerator_(a, x, y, t)) logger.info('Omega_ge: completed') return numerator, factors_denominator