def _matrix_(self, R): r""" Return Sage matrix from this octave element. EXAMPLES:: sage: A = octave('[1,2;3,4]') # optional - octave sage: matrix(ZZ, A) # optional - octave [1 2] [3 4] sage: A = octave('[1,2;3,4.5]') # optional - octave sage: matrix(RR, A) # optional - octave [1.00000000000000 2.00000000000000] [3.00000000000000 4.50000000000000] """ from sage.matrix.all import MatrixSpace s = str(self).strip() v = s.split('\n ') nrows = len(v) if nrows == 0: return MatrixSpace(R,0,0)(0) ncols = len(v[0].split()) M = MatrixSpace(R, nrows, ncols) v = sum([[x for x in w.split()] for w in v], []) return M(v)
def _creation_by_determinant_helper(self, k, part): """ EXAMPLES:: sage: S = MacdonaldPolynomialsS(QQ) sage: a = S([2,1]) sage: a._creation_by_determinant_helper(2,[1]) (q^3*t-q^2*t-q+1)*McdS[2, 1] + (q^3-q^2*t-q+t)*McdS[3] """ S = self.parent() q,t = S.q, S.t part += [0]*(k-len(part)) if len(part) > k: raise ValueError, "the column to add is too small" #Create the matrix over the homogeneous symmetric #functions and take its determinant MS = MatrixSpace(sfa.SFAHomogeneous(S.base_ring()), k, k) h = MS.base_ring() m = [] for i in range(k): row = [0]*max(0, (i+1)-2-part[i]) for j in range(max(0, (i+1)-2-part[i]),k): value = part[i]+j-i+1 p = [value] if value > 0 else [] row.append( (1-q**(part[i]+j-i+1)*t**(k-(j+1)))*h(p) ) m.append(row) M = MS(m) res = M.det() #Convert to the Schurs res = S._s( res ) return S._from_element(res)
def bilinear_form(self, R=None): """ Return the bilinear form over ``R`` associated to ``self``. INPUT: - ``R`` -- (default: universal cyclotomic field) a ring used to compute the bilinear form EXAMPLES:: sage: CoxeterType(['A', 2, 1]).bilinear_form() [ 1 -1/2 -1/2] [-1/2 1 -1/2] [-1/2 -1/2 1] sage: CoxeterType(['H', 3]).bilinear_form() [ 1 -1/2 0] [ -1/2 1 1/2*E(5)^2 + 1/2*E(5)^3] [ 0 1/2*E(5)^2 + 1/2*E(5)^3 1] sage: C = CoxeterMatrix([[1,-1,-1],[-1,1,-1],[-1,-1,1]]) sage: C.bilinear_form() [ 1 -1 -1] [-1 1 -1] [-1 -1 1] """ n = self.rank() mat = self.coxeter_matrix()._matrix base_ring = mat.base_ring() from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField UCF = UniversalCyclotomicField() if R is None: R = UCF # if UCF.has_coerce_map_from(base_ring): # R = UCF # else: # R = base_ring # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. if R is UCF: val = lambda x: (R.gen(2 * x) + ~R.gen(2 * x)) / R( -2) if x > -1 else R.one() * x else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: -R(cos(pi / SR(x))) if x > -1 else x MS = MatrixSpace(R, n, sparse=True) MC = MS._get_matrix_class() bilinear = MC(MS, entries={(i, j): val(mat[i, j]) for i in range(n) for j in range(n) if mat[i, j] != 2}, coerce=True, copy=True) bilinear.set_immutable() return bilinear
def _matrix_(self, R): r""" Return \sage matrix from this scilab element. EXAMPLES: sage: A = scilab('[1,2;3,4]') # optional - scilab sage: matrix(ZZ, A) # optional - scilab [1 2] [3 4] sage: A = scilab('[1,2;3,4.5]') # optional - scilab sage: matrix(RR, A) # optional - scilab [1.00000000000000 2.00000000000000] [3.00000000000000 4.50000000000000] """ from sage.matrix.all import MatrixSpace from sage.rings.all import ZZ s = str(self).strip() v = s.split('\n ') nrows = len(v) if nrows == 0: return MatrixSpace(R,0,0)(0) ncols = len(v[0].split()) M = MatrixSpace(R, nrows, ncols) v = sum([[x.rstrip('.') for x in w.split()] for w in v], []) return M(v)
def bilinear_form(self, R=None): """ Return the bilinear form over ``R`` associated to ``self``. INPUT: - ``R`` -- (default: universal cyclotomic field) a ring used to compute the bilinear form EXAMPLES:: sage: CoxeterType(['A', 2, 1]).bilinear_form() [ 1 -1/2 -1/2] [-1/2 1 -1/2] [-1/2 -1/2 1] sage: CoxeterType(['H', 3]).bilinear_form() [ 1 -1/2 0] [ -1/2 1 1/2*E(5)^2 + 1/2*E(5)^3] [ 0 1/2*E(5)^2 + 1/2*E(5)^3 1] sage: C = CoxeterMatrix([[1,-1,-1],[-1,1,-1],[-1,-1,1]]) sage: C.bilinear_form() [ 1 -1 -1] [-1 1 -1] [-1 -1 1] """ n = self.rank() mat = self.coxeter_matrix()._matrix base_ring = mat.base_ring() from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField UCF = UniversalCyclotomicField() if UCF.has_coerce_map_from(base_ring): R = UCF else: R = base_ring # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. if R is UCF: val = lambda x: (R.gen(2 * x) + ~R.gen(2 * x)) / R(-2) if x > -1 else R.one() * x else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: -R(cos(pi / SR(x))) if x > -1 else x MS = MatrixSpace(R, n, sparse=True) MC = MS._get_matrix_class() bilinear = MC( MS, entries={(i, j): val(mat[i, j]) for i in range(n) for j in range(n) if mat[i, j] != 2}, coerce=True, copy=True, ) bilinear.set_immutable() return bilinear
def _matrix_(self, R=None): r""" Return Sage matrix from this octave element. EXAMPLES:: sage: A = octave('[1,2;3,4.5]') # optional - octave sage: matrix(A) # optional - octave [1.0 2.0] [3.0 4.5] sage: _.base_ring() # optional - octave Real Double Field sage: A = octave('[I,1;-1,0]') # optional - octave sage: matrix(A) # optional - octave [1.0*I 1.0] [ -1.0 0.0] sage: _.base_ring() # optional - octave Complex Double Field sage: A = octave('[1,2;3,4]') # optional - octave sage: matrix(ZZ, A) # optional - octave [1 2] [3 4] sage: A = octave('[1,2;3,4.5]') # optional - octave sage: matrix(RR, A) # optional - octave [1.00000000000000 2.00000000000000] [3.00000000000000 4.50000000000000] """ oc = self.parent() if not self.ismatrix(): raise TypeError('not an octave matrix') if R is None: R = self._get_sage_ring() s = str(self).strip('\n ') w = [u.strip().split(' ') for u in s.split('\n')] nrows = len(w) ncols = len(w[0]) if self.iscomplex(): w = [[to_complex(x,R) for x in row] for row in w] from sage.matrix.all import MatrixSpace s = str(self).strip() v = s.split('\n ') nrows = len(v) if nrows == 0: return MatrixSpace(R,0,0)(0) ncols = len(v[0].split()) M = MatrixSpace(R, nrows, ncols) v = sum([[x for x in w.split()] for w in v], []) return M(v)
def matrix_clean_up(mat): r""" Apply get_cleaned_up_version to each object within each entry and return a cleaned up matrix """ dim = mat.nrows() L = list() mat_space = MatrixSpace(CombinatorialScalarRing(),dim) prnt = mat_space.base_ring() for i in range(dim): L.append(list()) for j in range(dim): L[i].append(CombinatorialScalarWrapper(mat[i,j].get_cleaned_up_version())) return mat_space(L)
def identity_matrix(dim): r""" Returns standard combinatorial identity matrix """ mat_space = MatrixSpace(CombinatorialScalarRing(),dim) prnt = mat_space.base_ring() L = list() for i in range(dim): L.append(list()) for j in range(dim): if i==j: L[i].append(prnt._one()) else: L[i].append(prnt._zero()) return mat_space(L)
def matrix_clean_up(mat): r""" Apply get_cleaned_up_version to each object within each entry and return a cleaned up matrix """ dim = mat.nrows() L = list() mat_space = MatrixSpace(CombinatorialScalarRing(), dim) prnt = mat_space.base_ring() for i in range(dim): L.append(list()) for j in range(dim): L[i].append( CombinatorialScalarWrapper(mat[i, j].get_cleaned_up_version())) return mat_space(L)
def identity_matrix(dim): r""" Returns standard combinatorial identity matrix """ mat_space = MatrixSpace(CombinatorialScalarRing(), dim) prnt = mat_space.base_ring() L = list() for i in range(dim): L.append(list()) for j in range(dim): if i == j: L[i].append(prnt._one()) else: L[i].append(prnt._zero()) return mat_space(L)
def linear_space(self): r""" Return the space of the affine transformations represented as linear transformations. We can represent affine transformations `Ax + b` as linear transformations by .. MATH:: \begin{pmatrix} A & b \\ 0 & 1 \end{pmatrix} and lifting `x = (x_1, \ldots, x_n)` to `(x_1, \ldots, x_n, 1)`. .. SEEALSO:: - :meth:`sage.groups.affine_gps.group_element.AffineGroupElement.matrix()` EXAMPLES:: sage: G = AffineGroup(3, GF(5)) sage: G.linear_space() Full MatrixSpace of 4 by 4 dense matrices over Finite Field of size 5 """ dp = self.degree() + 1 return MatrixSpace(self.base_ring(), dp, dp)
def coxeter_matrix(t): """ Returns the Coxeter matrix of type t. EXAMPLES:: sage: coxeter_matrix(['A', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 3] [2 2 3 1] sage: coxeter_matrix(['B', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 4] [2 2 4 1] sage: coxeter_matrix(['C', 4]) [1 3 2 2] [3 1 3 2] [2 3 1 4] [2 2 4 1] sage: coxeter_matrix(['D', 4]) [1 3 2 2] [3 1 3 3] [2 3 1 2] [2 3 2 1] :: sage: coxeter_matrix(['E', 6]) [1 2 3 2 2 2] [2 1 2 3 2 2] [3 2 1 3 2 2] [2 3 3 1 3 2] [2 2 2 3 1 3] [2 2 2 2 3 1] :: sage: coxeter_matrix(['F', 4]) [1 3 2 2] [3 1 4 2] [2 4 1 3] [2 2 3 1] :: sage: coxeter_matrix(['G', 2]) [1 6] [6 1] """ ct = CartanType(t) cf = coxeter_matrix_as_function(ct) index_set = ct.index_set() MS = MatrixSpace(ZZ, len(index_set)) m = MS(0) for i in range(len(index_set)): for j in range(len(index_set)): m[i, j] = cf(index_set[i], index_set[j]) return m
def matrix_adjoint_lemma_40(mat): r""" Returns a matrix which is the target in the reduction of adjoint(A) times A to det(A) times I. """ if mat.nrows() != mat.ncols(): raise ValueError, "Make sure that this is indeed a Combinatorial Adjoint matrix" else: dim = mat.nrows() L = list() mat_space = MatrixSpace(CombinatorialScalarRing(), dim) for i in range(dim): L.append(list()) for j in range(dim): if i == j: copyset = deepcopy(mat[i, j].get_set()) for elm in copyset: tmp = list(elm.get_object()[0].get_object()) #object is tuple, elements come from the actual tuple, hence double get_object() index = tmp.index(CombinatorialObject('_', 1)) tmp[index] = elm.get_object()[1] elm.set_object(tuple(tmp)) else: copyset = CombinatorialScalarWrapper(set()) L[i].append(CombinatorialScalarWrapper(copyset)) return mat_space(L)
def matrix_combinatorial_adjoint(mat): r""" Return Combinatorial Adjoint. """ dim = mat.nrows() M = list() mat_space = MatrixSpace(CombinatorialScalarRing(), dim) prnt = mat_space.base_ring() #create list L of lists of sets, dimension is increased by one to mitigate index confusion L = list() for i in range(dim + 1): L.append(list()) for j in range(dim + 1): L[i].append(set()) P = Permutations(dim) for p in P: p_comb = CombinatorialScalarWrapper( [CombinatorialObject(p, p.signature())]) l = list() for i in range(1, dim + 1): l.append(mat[p(i) - 1, i - 1]) #This list will have empty sets, which will yield to an empty cartesian product #especially when the matrix input is triangular (except for the identity permutation). #We will now iterate through the selected entries in each column #and create a set of a singleton of an empty string that corresponds #to the "missing" element of the tuple described in definition 39. for i in range(1, dim + 1): copy_l = copy(l) copy_l[i - 1] = CombinatorialScalarWrapper( [CombinatorialObject('_', 1)]) cp = CartesianProduct(p_comb, *copy_l) for tupel in cp: tupel = tuple(tupel) tupel_weight = 1 tupel_sign = 1 for elm in tupel: tupel_sign = tupel_sign * elm.get_sign() tupel_weight = tupel_weight * elm.get_weight() L[i][p(i)].add( CombinatorialObject(tupel, tupel_sign, tupel_weight)) #turn these sets into CombinatorialScalars for i in range(1, dim + 1): l = list() for j in range(1, dim + 1): l.append(CombinatorialScalarWrapper(L[i][j])) M.append(l) return mat_space(M)
def module_composition_factors(self, algorithm=None): r""" Returns a list of triples consisting of [base field, dimension, irreducibility], for each of the Meataxe composition factors modules. The algorithm="verbose" option returns more information, but in Meataxe notation. EXAMPLES:: sage: F=GF(3);MS=MatrixSpace(F,4,4) sage: M=MS(0) sage: M[0,1]=1;M[1,2]=1;M[2,3]=1;M[3,0]=1 sage: G = MatrixGroup([M]) sage: G.module_composition_factors() [(Finite Field of size 3, 1, True), (Finite Field of size 3, 1, True), (Finite Field of size 3, 2, True)] sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.module_composition_factors() [(Finite Field of size 7, 2, True)] Type "G.module_composition_factors(algorithm='verbose')" to get a more verbose version. For more on MeatAxe notation, see http://www.gap-system.org/Manuals/doc/htm/ref/CHAP067.htm """ from sage.misc.sage_eval import sage_eval F = self.base_ring() if not (F.is_finite()): raise NotImplementedError, "Base ring must be finite." q = F.cardinality() gens = self.gens() n = self.degree() MS = MatrixSpace(F, n, n) mats = [] # initializing list of mats by which the gens act on self W = self.matrix_space().row_space() for g in gens: p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("M:=GModuleByMats(" + mats_str + ", GF(" + str(q) + "))") gap.eval("MCFs := MTX.CompositionFactors( M )") N = eval(gap.eval("Length(MCFs)")) if algorithm == "verbose": print gap.eval('MCFs') + "\n" L = [] for i in range(1, N + 1): gap.eval("MCF := MCFs[%s]" % i) L.append( tuple([ sage_eval(gap.eval("MCF.field")), eval(gap.eval("MCF.dimension")), sage_eval(gap.eval("MCF.IsIrreducible")) ])) return sorted(L)
def get_matrix_B(self): dim = self.get_dim() mat_space = MatrixSpace(CombinatorialScalarRing(), dim) L = list() for i in range(dim): L.append(list()) for j in range(dim): L[i].append(self[i, j].get_B()) return mat_space(L)
def matrix_combinatorial_adjoint(mat): r""" Return Combinatorial Adjoint. """ dim = mat.nrows() M = list() mat_space = MatrixSpace(CombinatorialScalarRing(),dim) prnt = mat_space.base_ring() #create list L of lists of sets, dimension is increased by one to mitigate index confusion L = list() for i in range(dim+1): L.append(list()) for j in range(dim+1): L[i].append(set()) P = Permutations(dim) for p in P: p_comb = CombinatorialScalarWrapper([CombinatorialObject(p,p.signature())]) l = list() for i in range(1,dim+1): l.append(mat[p(i)-1,i-1]) #This list will have empty sets, which will yield to an empty cartesian product #especially when the matrix input is triangular (except for the identity permutation). #We will now iterate through the selected entries in each column #and create a set of a singleton of an empty string that corresponds #to the "missing" element of the tuple described in definition 39. for i in range(1,dim+1): copy_l = copy(l) copy_l[i-1]=CombinatorialScalarWrapper([CombinatorialObject('_',1)]) cp = CartesianProduct(p_comb,*copy_l) for tupel in cp: tupel = tuple(tupel) tupel_weight = 1 tupel_sign = 1 for elm in tupel: tupel_sign = tupel_sign*elm.get_sign() tupel_weight = tupel_weight*elm.get_weight() L[i][p(i)].add(CombinatorialObject(tupel,tupel_sign,tupel_weight)) #turn these sets into CombinatorialScalars for i in range(1,dim+1): l = list() for j in range(1,dim+1): l.append(CombinatorialScalarWrapper(L[i][j])) M.append(l) return mat_space(M)
def find_mod2pow_splitting(i): P = F.primes_above(2)[0] if i == 1: R = ResidueRing(P, 1) M = MatrixSpace(R, 2) return [M(matrix_lift(a).list()) for a in find_mod2_splitting()] R = ResidueRing(P, i) M = MatrixSpace(R, 2) # arbitrary lift wbar = [M(matrix_lift(a).list()) for a in find_mod2pow_splitting(i - 1)] # Find lifts of wbar[1] and wbar[2] that have square -1 k = P.residue_field() Mk = MatrixSpace(k, 2) t = 2**(i - 1) s = M(t) L = [] for j in [1, 2]: C = Mk(matrix_lift(wbar[j]**2 + M(1)) / t) A = Mk(matrix_lift(wbar[j])) # Find all matrices B in Mk such that AB+BA=C. L.append([ wbar[j] + s * M(matrix_lift(B)) for B in Mk if A * B + B * A == C ]) g = M(F.gen()) t = M(t) two = M(2) ginv = M(F.gen()**(-1)) for w1 in L[0]: for w2 in L[1]: w0 = ginv * (two * g * wbar[3] - w1 - w2 - w1 * w2) w3 = g * w0 + w2 * w1 if w0 * w0 != w0 - M(1): continue if w3 * w3 != M(-1): continue return w0, w1, w2, w3 raise ValueError
def _creation_by_determinant_helper(self, k, part): """ EXAMPLES:: sage: S = MacdonaldPolynomialsS(QQ) sage: a = S([2,1]) sage: a._creation_by_determinant_helper(2,[1]) (q^3*t-q^2*t-q+1)*McdS[2, 1] + (q^3-q^2*t-q+t)*McdS[3] """ S = self.parent() q, t = S.q, S.t part += [0] * (k - len(part)) if len(part) > k: raise ValueError, "the column to add is too small" #Create the matrix over the homogeneous symmetric #functions and take its determinant MS = MatrixSpace(sfa.SFAHomogeneous(S.base_ring()), k, k) h = MS.base_ring() m = [] for i in range(k): row = [0] * max(0, (i + 1) - 2 - part[i]) for j in range(max(0, (i + 1) - 2 - part[i]), k): value = part[i] + j - i + 1 p = [value] if value > 0 else [] row.append( (1 - q**(part[i] + j - i + 1) * t**(k - (j + 1))) * h(p)) m.append(row) M = MS(m) res = M.det() #Convert to the Schurs res = S._s(res) return S._from_element(res)
def on_left_matrix(self, new_BR=None): """ Returns the matrix of the action of self on the algebra my multiplication on the left If new_BR is specified, then the matrix will be over new_BR. TODO: split into to parts - build the endomorphism of multiplication on the left - build the matrix of an endomorphism EXAMPLES:: sage: QS3 = SymmetricGroupAlgebra(QQ, 3) sage: a = QS3([2,1,3]) sage: a.on_left_matrix() [0 0 1 0 0 0] [0 0 0 0 1 0] [1 0 0 0 0 0] [0 0 0 0 0 1] [0 1 0 0 0 0] [0 0 0 1 0 0] sage: a.on_left_matrix(RDF) [0.0 0.0 1.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0 1.0 0.0] [1.0 0.0 0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0 0.0 1.0] [0.0 1.0 0.0 0.0 0.0 0.0] [0.0 0.0 0.0 1.0 0.0 0.0] AUTHOR: Mike Hansen """ parent = self.parent() if parent.get_order() is None: cc = parent._combinatorial_class else: cc = parent.get_order() BR = parent.base_ring() if new_BR is None: new_BR = BR from sage.matrix.all import MatrixSpace MS = MatrixSpace(new_BR, parent.dimension(), parent.dimension()) l = [(self * parent(m)).to_vector() for m in cc] return MS(l).transpose()
def character_table(self): r""" Return the matrix of values of the irreducible characters of this group `G` at its conjugacy classes. The columns represent the conjugacy classes of `G` and the rows represent the different irreducible characters in the ordering given by GAP. OUTPUT: a matrix defined over a cyclotomic field EXAMPLES:: sage: MatrixGroup(SymmetricGroup(2)).character_table() [ 1 -1] [ 1 1] sage: MatrixGroup(SymmetricGroup(3)).character_table() [ 1 1 -1] [ 2 -1 0] [ 1 1 1] sage: MatrixGroup(SymmetricGroup(5)).character_table() [ 1 -1 -1 1 -1 1 1] [ 4 0 1 -1 -2 1 0] [ 5 1 -1 0 -1 -1 1] [ 6 0 0 1 0 0 -2] [ 5 -1 1 0 1 -1 1] [ 4 0 -1 -1 2 1 0] [ 1 1 1 1 1 1 1] """ #code from function in permgroup.py, but modified for #how gap handles these groups. G = self._gap_() cl = self.conjugacy_classes() from sage.rings.integer import Integer n = Integer(len(cl)) irrG = G.Irr() ct = [[irrG[i][j] for j in range(n)] for i in range(n)] from sage.rings.all import CyclotomicField e = irrG.Flat().Conductor() K = CyclotomicField(e) ct = [[K(x) for x in v] for v in ct] # Finally return the result as a matrix. from sage.matrix.all import MatrixSpace MS = MatrixSpace(K, n) return MS(ct)
def matrix_space(self): """ Return the matrix space corresponding to this matrix group. This is a matrix space over the field of definition of this matrix group. EXAMPLES:: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: G = MatrixGroup([MS(1), MS([1,2,3,4])]) sage: G.matrix_space() Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 5 sage: G.matrix_space() is MS True """ return MatrixSpace(self.base_ring(), self.degree())
def solve_linear_system(self, A, b): """ Use octave to compute a solution x to A\*x = b, as a list. INPUT: - ``A`` - mxn matrix A with entries in QQ or RR - ``b`` - m-vector b entries in QQ or RR (resp) OUTPUT: An list x (if it exists) which solves M\*x = b EXAMPLES:: sage: M33 = MatrixSpace(QQ,3,3) sage: A = M33([1,2,3,4,5,6,7,8,0]) sage: V3 = VectorSpace(QQ,3) sage: b = V3([1,2,3]) sage: octave.solve_linear_system(A,b) # optional - octave (and output is slightly random in low order bits) [-0.33333299999999999, 0.66666700000000001, -3.5236600000000002e-18] AUTHORS: - David Joyner and William Stein """ m = A.nrows() n = A.ncols() if m != len(b): raise ValueError("dimensions of A and b must be compatible") from sage.matrix.all import MatrixSpace from sage.rings.all import QQ MS = MatrixSpace(QQ, m, 1) b = MS(list(b)) # converted b to a "column vector" sA = self.sage2octave_matrix_string(A) sb = self.sage2octave_matrix_string(b) self.eval("a = " + sA) self.eval("b = " + sb) soln = octave.eval("c = a \\ b") soln = soln.replace("\n\n ", "[") soln = soln.replace("\n\n", "]") soln = soln.replace("\n", ",") sol = soln[3:] return eval(sol)
def matrix_space(self): """ Return the space of matrices representing the general linear transformations. OUTPUT: The parent of the matrices `A` defining the affine group element `Ax+b`. EXAMPLES:: sage: G = AffineGroup(3, GF(5)) sage: G.matrix_space() Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 5 """ d = self.degree() return MatrixSpace(self.base_ring(), d, d)
def hamilton_quatalg(R): """ Hamilton quaternion algebra over the commutative ring R, constructed as a free algebra quotient. INPUT: - R -- a commutative ring OUTPUT: - Q -- quaternion algebra - gens -- generators for Q EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(ZZ) sage: H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Integer Ring sage: i^2 -1 sage: i in H True Note that there is another vastly more efficient models for quaternion algebras in Sage; the one here is mainly for testing purposes:: sage: R.<i,j,k> = QuaternionAlgebra(QQ,-1,-1) # much fast than the above """ n = 3 from sage.algebras.free_algebra import FreeAlgebra from sage.matrix.all import MatrixSpace A = FreeAlgebra(R, n, 'i') F = A.monoid() i, j, k = F.gens() mons = [F(1), i, j, k] M = MatrixSpace(R, 4) mats = [ M([0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0]), M([0, 0, 1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, -1, 0, 0]), M([0, 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, -1, 0, 0, 0]) ] H3 = FreeAlgebraQuotient(A, mons, mats, names=('i', 'j', 'k')) return H3, H3.gens()
def matrix_space(self): """ Return the matrix space corresponding to this matrix group. This is a matrix space over the field of definition of this matrix group. EXAMPLES:: sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: G = MatrixGroup([MS(1), MS([1,2,3,4])]) sage: G.matrix_space() Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 5 """ try: return self.__matrix_space except AttributeError: pass self.__matrix_space = MatrixSpace(self.field_of_definition(), self.__n) return self.__matrix_space
def find_mod2_splitting(): P = F.primes_above(2)[0] k = P.residue_field() M = MatrixSpace(k, 2) V = k**4 g = k.gen() # image of golden ratio m1 = M(-1) sqrt_minus_1 = [(w, V(w.list())) for w in M if w * w == m1] one = M(1) v_one = V(one.list()) for w1, v1 in sqrt_minus_1: for w2, v2 in sqrt_minus_1: w0 = (g - 1) * (w1 + w2 - w1 * w2) w3 = g * w0 + w2 * w1 if w0 * w0 != w0 - 1: continue if w3 * w3 != -1: continue if V.span([w0.list(), v1, v2, w3.list()]).dimension() == 4: return w0, w1, w2, w3
def as_permutation_group(self, algorithm=None): r""" Return a permutation group representation for the group. In most cases occurring in practice, this is a permutation group of minimal degree (the degree begin determined from orbits under the group action). When these orbits are hard to compute, the procedure can be time-consuming and the degree may not be minimal. INPUT: - ``algorithm`` -- ``None`` or ``'smaller'``. In the latter case, try harder to find a permutation representation of small degree. OUTPUT: A permutation group isomorphic to ``self``. The ``algorithm='smaller'`` option tries to return an isomorphic group of low degree, but is not guaranteed to find the smallest one. EXAMPLES:: sage: MS = MatrixSpace(GF(2), 5, 5) sage: A = MS([[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]]) sage: G = MatrixGroup([A]) sage: G.as_permutation_group() Permutation Group with generators [(1,2)] sage: MS = MatrixSpace( GF(7), 12, 12) sage: GG = gap("ImfMatrixGroup( 12, 3 )") sage: GG.GeneratorsOfGroup().Length() 3 sage: g1 = MS(eval(str(GG.GeneratorsOfGroup()[1]).replace("\n",""))) sage: g2 = MS(eval(str(GG.GeneratorsOfGroup()[2]).replace("\n",""))) sage: g3 = MS(eval(str(GG.GeneratorsOfGroup()[3]).replace("\n",""))) sage: G = MatrixGroup([g1, g2, g3]) sage: G.cardinality() 21499084800 sage: set_random_seed(0); current_randstate().set_seed_gap() sage: P = G.as_permutation_group() sage: P.cardinality() 21499084800 sage: P.degree() # random output 144 sage: set_random_seed(3); current_randstate().set_seed_gap() sage: Psmaller = G.as_permutation_group(algorithm="smaller") sage: Psmaller.cardinality() 21499084800 sage: Psmaller.degree() # random output 108 In this case, the "smaller" option returned an isomorphic group of lower degree. The above example used GAP's library of irreducible maximal finite ("imf") integer matrix groups to construct the MatrixGroup G over GF(7). The section "Irreducible Maximal Finite Integral Matrix Groups" in the GAP reference manual has more details. """ # Note that the output of IsomorphismPermGroup() depends on # memory locations and will change if you change the order of # doctests and/or architecture from sage.groups.perm_gps.permgroup import PermutationGroup if not self.is_finite(): raise NotImplementedError, "Group must be finite." n = self.degree() MS = MatrixSpace(self.base_ring(), n, n) mats = [] # initializing list of mats by which the gens act on self for g in self.gens(): p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("iso:=IsomorphismPermGroup(Group(" + mats_str + "))") if algorithm == "smaller": gap.eval( "small:= SmallerDegreePermutationRepresentation( Image( iso ) );" ) C = gap("Image( small )") else: C = gap("Image( iso )") return PermutationGroup(gap_group=C)
def half_integral_weight_modform_basis(chi, k, prec): r""" A basis for the space of weight `k/2` forms with character `\chi`. The modulus of `\chi` must be divisible by `16` and `k` must be odd and `>1`. INPUT: - ``chi`` - a Dirichlet character with modulus divisible by 16 - ``k`` - an odd integer = 1 - ``prec`` - a positive integer OUTPUT: a list of power series .. warning:: 1. This code is very slow because it requests computation of a basis of modular forms for integral weight spaces, and that computation is still very slow. 2. If you give an input prec that is too small, then the output list of power series may be larger than the dimension of the space of half-integral forms. EXAMPLES: We compute some half-integral weight forms of level 16\*7 :: sage: half_integral_weight_modform_basis(DirichletGroup(16*7).0^2,3,30) [q - 2*q^2 - q^9 + 2*q^14 + 6*q^18 - 2*q^21 - 4*q^22 - q^25 + O(q^30), q^2 - q^14 - 3*q^18 + 2*q^22 + O(q^30), q^4 - q^8 - q^16 + q^28 + O(q^30), q^7 - 2*q^15 + O(q^30)] The following illustrates that choosing too low of a precision can give an incorrect answer. :: sage: half_integral_weight_modform_basis(DirichletGroup(16*7).0^2,3,20) [q - 2*q^2 - q^9 + 2*q^14 + 6*q^18 + O(q^20), q^2 - q^14 - 3*q^18 + O(q^20), q^4 - 2*q^8 + 2*q^12 - 4*q^16 + O(q^20), q^7 - 2*q^8 + 4*q^12 - 2*q^15 - 6*q^16 + O(q^20), q^8 - 2*q^12 + 3*q^16 + O(q^20)] We compute some spaces of low level and the first few possible weights. :: sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 3, 10) [] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 5, 10) [q - 2*q^3 - 2*q^5 + 4*q^7 - q^9 + O(q^10)] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 7, 10) [q - 2*q^2 + 4*q^3 + 4*q^4 - 10*q^5 - 16*q^7 + 19*q^9 + O(q^10), q^2 - 2*q^3 - 2*q^4 + 4*q^5 + 4*q^7 - 8*q^9 + O(q^10), q^3 - 2*q^5 - 2*q^7 + 4*q^9 + O(q^10)] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 9, 10) [q - 2*q^2 + 4*q^3 - 8*q^4 + 14*q^5 + 16*q^6 - 40*q^7 + 16*q^8 - 57*q^9 + O(q^10), q^2 - 2*q^3 + 4*q^4 - 8*q^5 - 8*q^6 + 20*q^7 - 8*q^8 + 32*q^9 + O(q^10), q^3 - 2*q^4 + 4*q^5 + 4*q^6 - 10*q^7 - 16*q^9 + O(q^10), q^4 - 2*q^5 - 2*q^6 + 4*q^7 + 4*q^9 + O(q^10), q^5 - 2*q^7 - 2*q^9 + O(q^10)] This example once raised an error (see trac #5792). :: sage: half_integral_weight_modform_basis(trivial_character(16),9,10) [q - 2*q^2 + 4*q^3 - 8*q^4 + 4*q^6 - 16*q^7 + 48*q^8 - 15*q^9 + O(q^10), q^2 - 2*q^3 + 4*q^4 - 2*q^6 + 8*q^7 - 24*q^8 + O(q^10), q^3 - 2*q^4 - 4*q^7 + 12*q^8 + O(q^10), q^4 - 6*q^8 + O(q^10)] ALGORITHM: Basmaji (page 55 of his Essen thesis, "Ein Algorithmus zur Berechnung von Hecke-Operatoren und Anwendungen auf modulare Kurven", http://wstein.org/scans/papers/basmaji/). Let `S = S_{k+1}(\epsilon)` be the space of cusp forms of even integer weight `k+1` and character `\varepsilon = \chi \psi^{(k+1)/2}`, where `\psi` is the nontrivial mod-4 Dirichlet character. Let `U` be the subspace of `S \times S` of elements `(a,b)` such that `\Theta_2 a = \Theta_3 b`. Then `U` is isomorphic to `S_{k/2}(\chi)` via the map `(a,b) \mapsto a/\Theta_3`. """ if chi.modulus() % 16: raise ValueError, "the character must have modulus divisible by 16" if not k % 2: raise ValueError, "k (=%s) must be odd" % k if k < 3: raise ValueError, "k (=%s) must be at least 3" % k chi = chi.minimize_base_ring() psi = chi.parent()(DirichletGroup(4, chi.base_ring()).gen()) eps = chi * psi**((k + 1) // 2) eps = eps.minimize_base_ring() M = constructor.ModularForms(eps, (k + 1) // 2) C = M.cuspidal_subspace() B = C.basis() # This computation of S below -- of course --dominates the whole function. #from sage.misc.all import cputime #tm = cputime() #print "Computing basis..." S = [f.q_expansion(prec) for f in B] #print "Time to compute basis", cputime(tm) T2 = theta2_qexp(prec) T3 = theta_qexp(prec) n = len(S) MS = MatrixSpace(M.base_ring(), 2 * n, prec) A = copy(MS.zero_matrix()) for i in range(n): T2f = T2 * S[i] T3f = T3 * S[i] for j in range(prec): A[i, j] = T2f[j] A[n + i, j] = -T3f[j] B = A.kernel().basis() a_vec = [sum([b[i] * S[i] for i in range(n)]) for b in B] if len(a_vec) == 0: return [] R = a_vec[0].parent() t3 = R(T3) return [R(a) / t3 for a in a_vec]
def cartan_matrix(t): """ Returns the Cartan matrix corresponding to type t. EXAMPLES:: sage: cartan_matrix(['A', 4]) [ 2 -1 0 0] [-1 2 -1 0] [ 0 -1 2 -1] [ 0 0 -1 2] sage: cartan_matrix(['B', 6]) [ 2 -1 0 0 0 0] [-1 2 -1 0 0 0] [ 0 -1 2 -1 0 0] [ 0 0 -1 2 -1 0] [ 0 0 0 -1 2 -1] [ 0 0 0 0 -2 2] sage: cartan_matrix(['C', 4]) [ 2 -1 0 0] [-1 2 -1 0] [ 0 -1 2 -2] [ 0 0 -1 2] sage: cartan_matrix(['D', 6]) [ 2 -1 0 0 0 0] [-1 2 -1 0 0 0] [ 0 -1 2 -1 0 0] [ 0 0 -1 2 -1 -1] [ 0 0 0 -1 2 0] [ 0 0 0 -1 0 2] sage: cartan_matrix(['E',6]) [ 2 0 -1 0 0 0] [ 0 2 0 -1 0 0] [-1 0 2 -1 0 0] [ 0 -1 -1 2 -1 0] [ 0 0 0 -1 2 -1] [ 0 0 0 0 -1 2] sage: cartan_matrix(['E',7]) [ 2 0 -1 0 0 0 0] [ 0 2 0 -1 0 0 0] [-1 0 2 -1 0 0 0] [ 0 -1 -1 2 -1 0 0] [ 0 0 0 -1 2 -1 0] [ 0 0 0 0 -1 2 -1] [ 0 0 0 0 0 -1 2] sage: cartan_matrix(['E', 8]) [ 2 0 -1 0 0 0 0 0] [ 0 2 0 -1 0 0 0 0] [-1 0 2 -1 0 0 0 0] [ 0 -1 -1 2 -1 0 0 0] [ 0 0 0 -1 2 -1 0 0] [ 0 0 0 0 -1 2 -1 0] [ 0 0 0 0 0 -1 2 -1] [ 0 0 0 0 0 0 -1 2] sage: cartan_matrix(['F', 4]) [ 2 -1 0 0] [-1 2 -1 0] [ 0 -2 2 -1] [ 0 0 -1 2] This is different from MuPAD-Combinat, due to different node convention? :: sage: cartan_matrix(['G', 2]) [ 2 -3] [-1 2] sage: cartan_matrix(['A',1,1]) [ 2 -2] [-2 2] sage: cartan_matrix(['A', 3, 1]) [ 2 -1 0 -1] [-1 2 -1 0] [ 0 -1 2 -1] [-1 0 -1 2] sage: cartan_matrix(['B', 3, 1]) [ 2 0 -1 0] [ 0 2 -1 0] [-1 -1 2 -1] [ 0 0 -2 2] sage: cartan_matrix(['C', 3, 1]) [ 2 -1 0 0] [-2 2 -1 0] [ 0 -1 2 -2] [ 0 0 -1 2] sage: cartan_matrix(['D', 4, 1]) [ 2 0 -1 0 0] [ 0 2 -1 0 0] [-1 -1 2 -1 -1] [ 0 0 -1 2 0] [ 0 0 -1 0 2] sage: cartan_matrix(['E', 6, 1]) [ 2 0 -1 0 0 0 0] [ 0 2 0 -1 0 0 0] [-1 0 2 0 -1 0 0] [ 0 -1 0 2 -1 0 0] [ 0 0 -1 -1 2 -1 0] [ 0 0 0 0 -1 2 -1] [ 0 0 0 0 0 -1 2] sage: cartan_matrix(['E', 7, 1]) [ 2 -1 0 0 0 0 0 0] [-1 2 0 -1 0 0 0 0] [ 0 0 2 0 -1 0 0 0] [ 0 -1 0 2 -1 0 0 0] [ 0 0 -1 -1 2 -1 0 0] [ 0 0 0 0 -1 2 -1 0] [ 0 0 0 0 0 -1 2 -1] [ 0 0 0 0 0 0 -1 2] sage: cartan_matrix(['E', 8, 1]) [ 2 0 0 0 0 0 0 0 -1] [ 0 2 0 -1 0 0 0 0 0] [ 0 0 2 0 -1 0 0 0 0] [ 0 -1 0 2 -1 0 0 0 0] [ 0 0 -1 -1 2 -1 0 0 0] [ 0 0 0 0 -1 2 -1 0 0] [ 0 0 0 0 0 -1 2 -1 0] [ 0 0 0 0 0 0 -1 2 -1] [-1 0 0 0 0 0 0 -1 2] sage: cartan_matrix(['F', 4, 1]) [ 2 -1 0 0 0] [-1 2 -1 0 0] [ 0 -1 2 -1 0] [ 0 0 -2 2 -1] [ 0 0 0 -1 2] sage: cartan_matrix(['G', 2, 1]) [ 2 0 -1] [ 0 2 -3] [-1 -1 2] """ t = cartan_type.CartanType(t) dynkin_diagram = t.dynkin_diagram() index_set = t.index_set() MS = MatrixSpace(ZZ, len(index_set), sparse=True) m = MS(0) for i in range(len(index_set)): for j in range(len(index_set)): m[i, j] = dynkin_diagram[index_set[i], index_set[j]] return m
def _compute(self, verbose=False): """ Computes the list of curves, the matrix and prime-degree isogenies. EXAMPLES:: sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve(K, [0,0,0,0,1]) sage: C = E.isogeny_class() sage: C2 = C.copy() sage: C2._mat sage: C2._compute() sage: C2._mat [1 3 6 2] [3 1 2 6] [6 2 1 3] [2 6 3 1] sage: C2._compute(verbose=True) possible isogeny degrees: [2, 3] -actual isogeny degrees: {2, 3} -added curve #1 (degree 2)... -added tuple [0, 1, 2]... -added tuple [1, 0, 2]... -added curve #2 (degree 3)... -added tuple [0, 2, 3]... -added tuple [2, 0, 3]...... relevant degrees: [2, 3]... -now completing the isogeny class... -processing curve #1... -added tuple [1, 0, 2]... -added tuple [0, 1, 2]... -added curve #3... -added tuple [1, 3, 3]... -added tuple [3, 1, 3]... -processing curve #2... -added tuple [2, 3, 2]... -added tuple [3, 2, 2]... -added tuple [2, 0, 3]... -added tuple [0, 2, 3]... -processing curve #3... -added tuple [3, 2, 2]... -added tuple [2, 3, 2]... -added tuple [3, 1, 3]... -added tuple [1, 3, 3]...... isogeny class has size 4 Sorting permutation = {0: 1, 1: 2, 2: 0, 3: 3} Matrix = [1 3 6 2] [3 1 2 6] [6 2 1 3] [2 6 3 1] TESTS: Check that :trac:`19030` is fixed (codomains of reverse isogenies were wrong):: sage: K.<i> = NumberField(x^2+1) sage: E = EllipticCurve([1, i + 1, 1, -72*i + 8, 95*i + 146]) sage: C = E.isogeny_class() sage: curves = C.curves sage: isos = C.isogenies() sage: isos[0][3].codomain() == curves[3] True """ from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix from sage.matrix.all import MatrixSpace from sage.sets.set import Set self._maps = None E = self.E.global_minimal_model(semi_global=True) degs = possible_isogeny_degrees(E) if verbose: import sys sys.stdout.write(" possible isogeny degrees: %s" % degs) sys.stdout.flush() isogenies = E.isogenies_prime_degree(degs) if verbose: sys.stdout.write(" -actual isogeny degrees: %s" % Set([phi.degree() for phi in isogenies])) sys.stdout.flush() # Add all new codomains to the list and collect degrees: curves = [E] ncurves = 1 degs = [] # tuples (i,j,l,phi) where curve i is l-isogenous to curve j via phi tuples = [] def add_tup(t): for T in [t, [t[1],t[0],t[2],0]]: if not T in tuples: tuples.append(T) if verbose: sys.stdout.write(" -added tuple %s..." % T[:3]) sys.stdout.flush() for phi in isogenies: E2 = phi.codomain() d = ZZ(phi.degree()) if not any([E2.is_isomorphic(E3) for E3 in curves]): curves.append(E2) if verbose: sys.stdout.write(" -added curve #%s (degree %s)..." % (ncurves,d)) sys.stdout.flush() add_tup([0,ncurves,d,phi]) ncurves += 1 if not d in degs: degs.append(d) if verbose: sys.stdout.write("... relevant degrees: %s..." % degs) sys.stdout.write(" -now completing the isogeny class...") sys.stdout.flush() i = 1 while i < ncurves: E1 = curves[i] if verbose: sys.stdout.write(" -processing curve #%s..." % i) sys.stdout.flush() isogenies = E1.isogenies_prime_degree(degs) for phi in isogenies: E2 = phi.codomain() d = phi.degree() js = [j for j,E3 in enumerate(curves) if E2.is_isomorphic(E3)] if js: # seen codomain already -- up to isomorphism j = js[0] if phi.codomain()!=curves[j]: iso = E2.isomorphism_to(curves[j]) phi.set_post_isomorphism(iso) assert phi.domain()==curves[i] and phi.codomain()==curves[j] add_tup([i,j,d,phi]) else: curves.append(E2) if verbose: sys.stdout.write(" -added curve #%s..." % ncurves) sys.stdout.flush() add_tup([i,ncurves,d,phi]) ncurves += 1 i += 1 if verbose: print("... isogeny class has size %s" % ncurves) # key function for sorting if E.has_rational_cm(): key_function = lambda E: (-E.cm_discriminant(),flatten([list(ai) for ai in E.ainvs()])) else: key_function = lambda E: flatten([list(ai) for ai in E.ainvs()]) self.curves = sorted(curves,key=key_function) perm = dict([(i,self.curves.index(E)) for i,E in enumerate(curves)]) if verbose: print("Sorting permutation = %s" % perm) mat = MatrixSpace(ZZ,ncurves)(0) self._maps = [[0]*ncurves for i in range(ncurves)] for i,j,l,phi in tuples: if phi!=0: mat[perm[i],perm[j]] = l self._maps[perm[i]][perm[j]] = phi self._mat = fill_isogeny_matrix(mat) if verbose: print("Matrix = %s" % self._mat) if not E.has_rational_cm(): self._qfmat = None return # In the CM case, we will have found some "horizontal" # isogenies of composite degree and would like to replace them # by isogenies of prime degree, mainly to make the isogeny # graph look better. We also construct a matrix whose entries # are not degrees of cyclic isogenies, but rather quadratic # forms (in 1 or 2 variables) representing the isogeny # degrees. For this we take a short cut: properly speaking, # when `\text{End}(E_1)=\text{End}(E_2)=O`, the set # `\text{Hom}(E_1,E_2)` is a rank `1` projective `O`-module, # hence has a well-defined ideal class associated to it, and # hence (using an identification between the ideal class group # and the group of classes of primitive quadratic forms of the # same discriminant) an equivalence class of quadratic forms. # But we currently only care about the numbers represented by # the form, i.e. which genus it is in rather than the exact # class. So it suffices to find one form of the correct # discriminant which represents one isogeny degree from `E_1` # to `E_2` in order to obtain a form which represents all such # degrees. if verbose: print("Creating degree matrix (CM case)") allQs = {} # keys: discriminants d # values: lists of equivalence classes of # primitive forms of discriminant d def find_quadratic_form(d,n): if not d in allQs: from sage.quadratic_forms.binary_qf import BinaryQF_reduced_representatives allQs[d] = BinaryQF_reduced_representatives(d, primitive_only=True) # now test which of the Qs represents n for Q in allQs[d]: if Q.solve_integer(n): return Q raise ValueError("No form of discriminant %d represents %s" %(d,n)) mat = self._mat qfmat = [[0 for i in range(ncurves)] for j in range(ncurves)] for i, E1 in enumerate(self.curves): for j, E2 in enumerate(self.curves): if j<i: qfmat[i][j] = qfmat[j][i] mat[i,j] = mat[j,i] elif i==j: qfmat[i][j] = [1] # mat[i,j] already 1 else: d = E1.cm_discriminant() if d != E2.cm_discriminant(): qfmat[i][j] = [mat[i,j]] # mat[i,j] already unique else: # horizontal isogeny q = find_quadratic_form(d,mat[i,j]) qfmat[i][j] = list(q) mat[i,j] = q.small_prime_value() self._mat = mat self._qfmat = qfmat if verbose: print("new matrix = %s" % mat) print("matrix of forms = %s" % qfmat)
def _compute(self): """ Computes the list of curves, and possibly the matrix and prime-degree isogenies (depending on the algorithm selected). EXAMPLES:: sage: isocls = EllipticCurve('48a1').isogeny_class('sage',use_tuple=False).copy() sage: isocls._mat sage: isocls._compute(); isocls._mat [0 2 2 2 0 0] [2 0 0 0 2 2] [2 0 0 0 0 0] [2 0 0 0 0 0] [0 2 0 0 0 0] [0 2 0 0 0 0] """ algorithm = self._algorithm from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix from sage.matrix.all import MatrixSpace self._maps = None if algorithm == "mwrank": try: E = self.E.mwrank_curve() except ValueError: E = self.E.minimal_model().mwrank_curve() I, A = E.isogeny_class(verbose=self._verbose) self._mat = MatrixSpace(ZZ, len(A))(A) self.curves = tuple( [constructor.EllipticCurve(ainvs) for ainvs in I]) elif algorithm == "database": try: label = self.E.cremona_label(space=False) except RuntimeError: raise RuntimeError("unable to to find %s in the database" % self.E) db = sage.databases.cremona.CremonaDatabase() curves = db.isogeny_class(label) if len(curves) == 0: raise RuntimeError("unable to to find %s in the database" % self.E) # All curves will have the same conductor and isogeny class, # and there are are most 8 of them, so lexicographic sorting is okay. self.curves = tuple(sorted(curves, key=lambda E: E.cremona_label())) self._mat = None elif algorithm == "sage": curves = [self.E.minimal_model()] ijl_triples = [] l_list = None i = 0 while i < len(curves): E = curves[i] isogs = E.isogenies_prime_degree(l_list) for phi in isogs: Edash = phi.codomain() l = phi.degree() # look to see if Edash is new. Note that the # curves returned by isogenies_prime_degree() are # standard minimal models, so it suffices to check # equality rather than isomorphism here. try: j = curves.index(Edash) except ValueError: j = len(curves) curves.append(Edash) ijl_triples.append((i, j, l, phi)) if l_list is None: l_list = [l for l in set([ZZ(f.degree()) for f in isogs])] i = i + 1 self.curves = tuple(curves) ncurves = len(curves) self._mat = MatrixSpace(ZZ, ncurves)(0) self._maps = [[0] * ncurves for i in range(ncurves)] for i, j, l, phi in ijl_triples: self._mat[i, j] = l self._maps[i][j] = phi else: raise ValueError, "unknown algorithm '%s'" % algorithm
def ag_code(C, D, E): r""" INPUT: C -- a plane curve over a finite field F of prime order D -- a divisor on C E -- P1 + ... + Pn, another divisor, a sum of distinct points on X whose support is disjoint from that of D and with n > deg(D) > 0. OUTPUT: The linear code defined by C, D, and E as in Stichtenoth. Calls Singular's \code{BrillNoether functions}. Will break on some singular curves or if the field size is too large, etc.; when this happens a ??? exception is raised. EXAMPLES: sage: x,y,z = ProjectiveSpace(2, GF(17), names = 'xyz').gens() sage: C = Curve(y^2*z^7 - x^9 - x*z^8) sage: pts = C.rational_points(sorted=False) sage: D = C.divisor([(3,pts[0]), (-1,pts[1]), (10,pts[5])]); D 3*(0 : 0 : 1) - (0 : 1 : 0) + 10*(11 : 0 : 1) sage: E = add([C.divisor(p) for p in [pts[2],pts[3],pts[4]]]); E (1 : 11 : 1) + (1 : 6 : 1) + (10 : 0 : 1) sage: V = ag_code(C, D,E) sage: V Linear code of length 2, dimension 2 over Finite Field of size 17 sage: V.basis() [(12, 12), (11, 11), (12, 12), (2, 2), (2, 2), (3, 14), (13, 4), (7, 7), (7, 10)] sage: P2 = ProjectiveSpace(2, GF(11), names = ['x','y','z']) sage: x, y, z = P2.coordinate_ring().gens() sage: f = y^8 + x^8 - z^8 sage: C = Curve(f) sage: pts = C.rational_points() sage: D = C.divisor(pts[0])*10 - C.divisor(pts[1]) + C.divisor(pts[5])*10 sage: E = add([C.divisor(pts[i]) for i in [2,3,4,6,7,8,9,10,11]]) sage: V = ag_code(C, D,E); V Linear code of length 8, dimension 2 over Finite Field of size 11 sage: V.basis() [(6, 6), (11, 11), (13, 13), (13, 13), (15, 15), (8, 9), (3, 14), (7, 7), (9, 8)] sage: P2 = ProjectiveSpace(2, GF(11), names = ['x','y','z']) sage: x, y, z = P2.coordinate_ring().gens() sage: f = x^3*y - y^3*z + z^3*x sage: C = Curve(f) sage: pts = C.rational_points(sorted=False) sage: D = C.divisor(pts[0])*3 - C.divisor(pts[1]) + C.divisor(pts[5])*5 sage: len(C.riemann_roch_basis(D)) 5 sage: E = add([C.divisor(pts[i]) for i in [2,3,4,6,7,8,9,10,11]]) sage: V = ag_code(C, D,E); V Linear code of length 8, dimension 5 over Finite Field of size 11 sage: V.basis() [(1, 8, 4, 1, 10, 1, 5, 8), (2, 8, 1, 7, 3, 5, 8, 2), (1, 7, 4, 7, 5, 7, 4, 9), (0, 7, 0, 9, 5, 8, 6, 3), (6, 10, 1, 4, 6, 4, 8, 0)] sage: V.minimum_distance() 3 AUTHOR: David Joyner (2006-01) """ F = C.base_ring() one = F(1) B = C.riemann_roch_basis(D) k = len(B) if k == 0: A = MatrixSpace(F, 0, 0)(0) return sage.coding.all.LinearCode(A) P = C.rational_points(algorithm="bn", sorted=False) # algorithm="bn" does not work here .... N = len(P) pts = copy.copy(E.support()) for p in pts: for i in range(k): if (B[i].denominator())(F(p[0]), F(p[1]), F(p[2])) == 0: pts.remove(p) break MS = MatrixSpace(F, len(B), len(pts)) pts.sort() G = [[B[i](F(p[0]), F(p[1]), F(p[2])) for p in pts] for i in range(k)] G = MS(G) return linear_code.LinearCode(G)
def hecke_operator_on_basis(B, n, k, eps=None, already_echelonized=False): r""" Given a basis `B` of `q`-expansions for a space of modular forms with character `\varepsilon` to precision at least `\#B\cdot n+1`, this function computes the matrix of `T_n` relative to `B`. .. note:: If the elements of B are not known to sufficient precision, this function will report that the vectors are linearly dependent (since they are to the specified precision). INPUT: - ``B`` - list of q-expansions - ``n`` - an integer >= 1 - ``k`` - an integer - ``eps`` - Dirichlet character - ``already_echelonized`` -- bool (default: False); if True, use that the basis is already in Echelon form, which saves a lot of time. EXAMPLES:: sage: sage.modular.modform.constructor.ModularForms_clear_cache() sage: ModularForms(1,12).q_expansion_basis() [ q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 + O(q^6), 1 + 65520/691*q + 134250480/691*q^2 + 11606736960/691*q^3 + 274945048560/691*q^4 + 3199218815520/691*q^5 + O(q^6) ] sage: hecke_operator_on_basis(ModularForms(1,12).q_expansion_basis(), 3, 12) Traceback (most recent call last): ... ValueError: The given basis vectors must be linearly independent. sage: hecke_operator_on_basis(ModularForms(1,12).q_expansion_basis(30), 3, 12) [ 252 0] [ 0 177148] TESTS: This shows that the problem with finite fields reported at trac #8281 is solved:: sage: bas_mod5 = [f.change_ring(GF(5)) for f in victor_miller_basis(12, 20)] sage: hecke_operator_on_basis(bas_mod5, 2, 12) [4 0] [0 1] This shows that empty input is handled sensibly (trac #12202):: sage: x = hecke_operator_on_basis([], 3, 12); x [] sage: x.parent() Full MatrixSpace of 0 by 0 dense matrices over Cyclotomic Field of order 1 and degree 1 sage: y = hecke_operator_on_basis([], 3, 12, eps=DirichletGroup(13).0^2); y [] sage: y.parent() Full MatrixSpace of 0 by 0 dense matrices over Cyclotomic Field of order 12 and degree 4 """ if not isinstance(B, (list, tuple)): raise TypeError, "B (=%s) must be a list or tuple" % B if len(B) == 0: if eps is None: R = CyclotomicField(1) else: R = eps.base_ring() return MatrixSpace(R, 0)(0) f = B[0] R = f.base_ring() if eps is None: eps = DirichletGroup(1, R).gen(0) all_powerseries = True for x in B: if not is_PowerSeries(x): all_powerseries = False if not all_powerseries: raise TypeError, "each element of B must be a power series" n = Integer(n) k = Integer(k) prec = (f.prec() - 1) // n A = R**prec V = A.span_of_basis([g.padded_list(prec) for g in B], already_echelonized=already_echelonized) return _hecke_operator_on_basis(B, V, n, k, eps)
def half_integral_weight_modform_basis(chi, k, prec): r""" A basis for the space of weight `k/2` forms with character `\chi`. The modulus of `\chi` must be divisible by `16` and `k` must be odd and `>1`. INPUT: - ``chi`` - a Dirichlet character with modulus divisible by 16 - ``k`` - an odd integer = 1 - ``prec`` - a positive integer OUTPUT: a list of power series .. warning:: 1. This code is very slow because it requests computation of a basis of modular forms for integral weight spaces, and that computation is still very slow. 2. If you give an input prec that is too small, then the output list of power series may be larger than the dimension of the space of half-integral forms. EXAMPLES: We compute some half-integral weight forms of level 16\*7 :: sage: half_integral_weight_modform_basis(DirichletGroup(16*7).0^2,3,30) [q - 2*q^2 - q^9 + 2*q^14 + 6*q^18 - 2*q^21 - 4*q^22 - q^25 + O(q^30), q^2 - q^14 - 3*q^18 + 2*q^22 + O(q^30), q^4 - q^8 - q^16 + q^28 + O(q^30), q^7 - 2*q^15 + O(q^30)] The following illustrates that choosing too low of a precision can give an incorrect answer. :: sage: half_integral_weight_modform_basis(DirichletGroup(16*7).0^2,3,20) [q - 2*q^2 - q^9 + 2*q^14 + 6*q^18 + O(q^20), q^2 - q^14 - 3*q^18 + O(q^20), q^4 - 2*q^8 + 2*q^12 - 4*q^16 + O(q^20), q^7 - 2*q^8 + 4*q^12 - 2*q^15 - 6*q^16 + O(q^20), q^8 - 2*q^12 + 3*q^16 + O(q^20)] We compute some spaces of low level and the first few possible weights. :: sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 3, 10) [] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 5, 10) [q - 2*q^3 - 2*q^5 + 4*q^7 - q^9 + O(q^10)] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 7, 10) [q - 2*q^2 + 4*q^3 + 4*q^4 - 10*q^5 - 16*q^7 + 19*q^9 + O(q^10), q^2 - 2*q^3 - 2*q^4 + 4*q^5 + 4*q^7 - 8*q^9 + O(q^10), q^3 - 2*q^5 - 2*q^7 + 4*q^9 + O(q^10)] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 9, 10) [q - 2*q^2 + 4*q^3 - 8*q^4 + 14*q^5 + 16*q^6 - 40*q^7 + 16*q^8 - 57*q^9 + O(q^10), q^2 - 2*q^3 + 4*q^4 - 8*q^5 - 8*q^6 + 20*q^7 - 8*q^8 + 32*q^9 + O(q^10), q^3 - 2*q^4 + 4*q^5 + 4*q^6 - 10*q^7 - 16*q^9 + O(q^10), q^4 - 2*q^5 - 2*q^6 + 4*q^7 + 4*q^9 + O(q^10), q^5 - 2*q^7 - 2*q^9 + O(q^10)] This example once raised an error (see trac #5792). :: sage: half_integral_weight_modform_basis(trivial_character(16),9,10) [q - 2*q^2 + 4*q^3 - 8*q^4 + 4*q^6 - 16*q^7 + 48*q^8 - 15*q^9 + O(q^10), q^2 - 2*q^3 + 4*q^4 - 2*q^6 + 8*q^7 - 24*q^8 + O(q^10), q^3 - 2*q^4 - 4*q^7 + 12*q^8 + O(q^10), q^4 - 6*q^8 + O(q^10)] ALGORITHM: Basmaji (page 55 of his Essen thesis, "Ein Algorithmus zur Berechnung von Hecke-Operatoren und Anwendungen auf modulare Kurven", http://wstein.org/scans/papers/basmaji/). Let `S = S_{k+1}(\epsilon)` be the space of cusp forms of even integer weight `k+1` and character `\varepsilon = \chi \psi^{(k+1)/2}`, where `\psi` is the nontrivial mod-4 Dirichlet character. Let `U` be the subspace of `S \times S` of elements `(a,b)` such that `\Theta_2 a = \Theta_3 b`. Then `U` is isomorphic to `S_{k/2}(\chi)` via the map `(a,b) \mapsto a/\Theta_3`. """ if chi.modulus() % 16: raise ValueError, "the character must have modulus divisible by 16" if not k%2: raise ValueError, "k (=%s) must be odd"%k if k < 3: raise ValueError, "k (=%s) must be at least 3"%k chi = chi.minimize_base_ring() psi = chi.parent()(DirichletGroup(4, chi.base_ring()).gen()) eps = chi*psi**((k+1) // 2) eps = eps.minimize_base_ring() M = constructor.ModularForms(eps, (k+1)//2) C = M.cuspidal_subspace() B = C.basis() # This computation of S below -- of course --dominates the whole function. #from sage.misc.all import cputime #tm = cputime() #print "Computing basis..." S = [f.q_expansion(prec) for f in B] #print "Time to compute basis", cputime(tm) T2 = theta2_qexp(prec) T3 = theta_qexp(prec) n = len(S) MS = MatrixSpace(M.base_ring(), 2*n, prec) A = copy(MS.zero_matrix()) for i in range(n): T2f = T2*S[i] T3f = T3*S[i] for j in range(prec): A[i, j] = T2f[j] A[n+i, j] = -T3f[j] B = A.kernel().basis() a_vec = [sum([b[i]*S[i] for i in range(n)]) for b in B] if len(a_vec) == 0: return [] R = a_vec[0].parent() t3 = R(T3) return [R(a) / t3 for a in a_vec]