def perpendicular_bisector(self): #UHP r""" Return the perpendicular bisector of the hyperbolic geodesic ``self`` if that geodesic has finite length. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.random_geodesic() sage: h = g.perpendicular_bisector() sage: c = lambda x: x.coordinates() sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) True Infinite geodesics cannot be bisected:: sage: UHP.get_geodesic(0, 1).perpendicular_bisector() Traceback (most recent call last): ... ValueError: the length must be finite """ if self.length() == infinity: raise ValueError("the length must be finite") start = self._start.coordinates() d = self._model._dist_points(start, self._end.coordinates()) / 2 S = self.complete()._to_std_geod(start) T1 = matrix([[exp(d/2), 0], [0, exp(-d/2)]]) s2 = sqrt(2) * 0.5 T2 = matrix([[s2, -s2], [s2, s2]]) isom_mtrx = S.inverse() * (T1 * T2) * S # We need to clean this matrix up. if (isom_mtrx - isom_mtrx.conjugate()).norm() < 5*EPSILON: # Imaginary part is small. isom_mtrx = (isom_mtrx + isom_mtrx.conjugate()) / 2 # Set it to its real part. H = self._model.get_isometry(isom_mtrx) return self._model.get_geodesic(H(self._start), H(self._end))
def walsh_matrix(m0): """ This is the generator matrix of a Walsh code. The matrix of codewords correspond to a Hadamard matrix. EXAMPLES:: sage: walsh_matrix(2) [0 0 1 1] [0 1 0 1] sage: walsh_matrix(3) [0 0 0 0 1 1 1 1] [0 0 1 1 0 0 1 1] [0 1 0 1 0 1 0 1] sage: C = LinearCode(walsh_matrix(4)); C [16, 4] linear code over GF(2) sage: C.spectrum() [1, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0] This last code has minimum distance 8. REFERENCES: - http://en.wikipedia.org/wiki/Hadamard_matrix """ m = int(m0) if m == 1: return matrix(GF(2), 1, 2, [ 0, 1]) if m > 1: row2 = [x.list() for x in walsh_matrix(m-1).augment(walsh_matrix(m-1)).rows()] return matrix(GF(2), m, 2**m, [[0]*2**(m-1) + [1]*2**(m-1)] + row2) raise ValueError("%s must be an integer > 0."%m0)
def matrix_multiplicative_order(m): r""" Return the order of the 2x2 matrix ``m``. """ if m.is_one(): return Integer(1) elif m.det() != 1 and m.det() != -1: return Infinity # now we compute the potentially preserved quadratic form # i.e. looking for A such that m^t A m = A m00 = m[0,0] m01 = m[0,1] m10 = m[1,0] m11 = m[1,1] M = matrix(m.base_ring(), [[m00**2, m00*m10, m10**2], [m00*m01, m00*m11, m10*m11], [m01**2, m01*m11, m11**2]]) # might there be several solutions ? (other than scaling)... should not try: v = (M-identity_matrix(3)).solve_right() except ValueError: # no solution return False raise NotImplementedError("your matrix is conjugate to an orthogonal matrix but the angle might not be rational.. to be terminated.") # then we conjugate and check if the angles are rational # we need to take a square root of a symmetric matrix... this is not implemented! A = matrix(m.base_ring(), [[v[0],v[1]],[v[1],v[2]]])
def parity_check_matrix(self): r""" Returns a parity check matrix of ``self``. This matrix is computed directly from :func:`original_code`. EXAMPLES:: sage: set_random_seed(42) sage: C = codes.RandomLinearCode(9, 5, GF(7)) sage: Ce = codes.ExtendedCode(C) sage: Ce.parity_check_matrix() [1 1 1 1 1 1 1 1 1 1] [1 0 0 0 2 1 6 6 4 0] [0 1 0 0 6 1 6 1 0 0] [0 0 1 0 3 2 6 2 1 0] [0 0 0 1 4 5 4 3 5 0] """ F = self.base_ring() zero = F.zero() one = F.one() H = self.original_code().parity_check_matrix() nr, nc = H.nrows(), H.ncols() Hlist = H.list() v = matrix(F, nr + 1, 1, [one] + [zero] * nr) return matrix(F, nr + 1, nc, [one] * nc + Hlist).augment(v)
def orthonormal_1(dim_n=5): """ A matrix of rational approximations to orthonormal vectors to ``(1,...,1)``. INPUT: - ``dim_n`` - the dimension of the vectors OUTPUT: A matrix over ``QQ`` whose rows are close to an orthonormal basis to the subspace normal to ``(1,...,1)``. EXAMPLES:: sage: from sage.geometry.polyhedron.library import Polytopes sage: m = Polytopes.orthonormal_1(5) sage: m [ 70711/100000 -7071/10000 0 0 0] [ 1633/4000 1633/4000 -81649/100000 0 0] [ 7217/25000 7217/25000 7217/25000 -43301/50000 0] [ 22361/100000 22361/100000 22361/100000 22361/100000 -44721/50000] """ pb = [] for i in range(0,dim_n-1): pb.append([1.0/(i+1)]*(i+1) + [-1] + [0]*(dim_n-i-2)) m = matrix(RDF,pb) new_m = [] for i in range(0,dim_n-1): new_m.append([RDF(100000*q/norm(m[i])).ceil()/100000 for q in m[i]]) return matrix(QQ,new_m)
def image_mod_n(self): r""" Return the image of this group in `SL(2, \ZZ / N\ZZ)`. EXAMPLE:: sage: Gamma0(3).image_mod_n() Matrix group over Ring of integers modulo 3 with 2 generators ( [2 0] [1 1] [0 2], [0 1] ) TEST:: sage: for n in [2..20]: ... for g in Gamma0(n).gamma_h_subgroups(): ... G = g.image_mod_n() ... assert G.order() == Gamma(n).index() / g.index() """ N = self.level() if N == 1: raise NotImplementedError("Matrix groups over ring of integers modulo 1 not implemented") gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1)/x]) for x in self._generators_for_H()] gens += [matrix(Zmod(N),2,[1,1,0,1])] return MatrixGroup(gens)
def reflection_involution(self): r""" Return the isometry of the involution fixing the geodesic ``self``. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() sage: g1 = UHP.get_geodesic(0, 1) sage: g1.reflection_involution() Isometry in UHP [ 1 0] [ 2 -1] sage: UHP.get_geodesic(I, 2*I).reflection_involution() Isometry in UHP [ 1 0] [ 0 -1] """ x, y = [real(k.coordinates()) for k in self.ideal_endpoints()] if x == infinity: M = matrix([[1, -2*y], [0, -1]]) elif y == infinity: M = matrix([[1, -2*x], [0, -1]]) else: M = matrix([[(x+y)/(y-x), -2*x*y/(y-x)], [2/(y-x), -(x+y)/(y-x)]]) return self._model.get_isometry(M)
def parity_check_matrix(self): r""" Returns a parity check matrix of ``self``. This matrix is computed directly from :func:`original_code`. EXAMPLES:: sage: C = LinearCode(matrix(GF(2),[[1,0,0,1,1],\ [0,1,0,1,0],\ [0,0,1,1,1]])) sage: C.parity_check_matrix() [1 0 1 0 1] [0 1 0 1 1] sage: Ce = codes.ExtendedCode(C) sage: Ce.parity_check_matrix() [1 1 1 1 1 1] [1 0 1 0 1 0] [0 1 0 1 1 0] """ F = self.base_ring() zero = F.zero() one = F.one() H = self.original_code().parity_check_matrix() nr, nc = H.nrows(), H.ncols() Hlist = H.list() v = matrix(F, nr + 1, 1, [one] + [zero] * nr) M = matrix(F, nr + 1, nc, [one] * nc + Hlist).augment(v) M.set_immutable() return M
def kernel_vector(self, way='LLL', verbose=False): r""" todo: clean this EXAMPLES:: sage: from slabbe import ChristoffelGraph sage: C = ChristoffelGraph((2,5,7)) sage: C.kernel_vector() [(-1, -1, 1), (3, -4, 0)] """ from sage.arith.misc import gcd if way == 'vect_gcd': a,b,c = self._v gcd_ac = gcd(a,c) gcd_bc = gcd(b,c) U = ua,ub,uc = vector((c,0,-a)) / gcd(a,c) V = va,vb,vc = vector((0,c,-b)) / gcd(b,c) rows = U,V elif way == 'echelon': a,b,c = self._v m = matrix(ZZ, 4, [1,1,1,c,0,-a,0,c,-b,b,-a,0]) me = m.echelon_form() if verbose: print(me) rows = me[1],me[2] elif way == 'LLL': dim = self.dimension() if dim == 3: a,b,c = self._v M = matrix(ZZ, 4, [1,1,1,c,0,-a,0,c,-b,b,-a,0]) elif dim == 4: a,b,c,d = self._v M = matrix(ZZ, 7, (1,1,1,1,b,-a,0,0,c,0,-a,0,0,c,-b,0,d,0,0,-a,0,d,0,-b,0,0,d,-c)) else: raise ValueError("dimension (=%s) must be 3 or 4" % dim) rows = M.LLL().rows() VS = rows[0].parent() zero = VS(0) un = VS((1,)*dim) assert zero in rows, "(0,0,0) not in LLL result" assert un in rows, "(1,1,1) not in LLL result" while zero in rows: rows.remove(zero) while un in rows: rows.remove(un) elif way == 'vect': a,b,c = self._v U = ua,ub,uc = vector((c,0,-a)) V = va,vb,vc = vector((0,c,-b)) rows = U,V else: raise ValueError("unknown way") R = matrix(rows) if sum(map(abs, R.minors(dim-1))) != sum(map(abs,self._v)): print(R) print(R.minors(dim-1)) print(sum(map(abs, R.minors(dim)))) print(sum(map(abs,self._v))) raise Exception("The result (=%s) is false " % rows) return rows
def __repr__(self): r""" Return string representation. OUTPUT: String. EXAMPLES:: sage: from sage.geometry.polyhedron.double_description import \ ....: DoubleDescriptionPair, StandardAlgorithm sage: A = matrix(QQ, [(1,0,1), (0,1,1), (-1,-1,1)]) sage: DD = StandardAlgorithm(A).run() sage: DD.__repr__() 'Double description pair (A, R) defined by\n [ 1 0 1] [ 2/3 -1/3 -1/3]\nA = [ 0 1 1], R = [-1/3 2/3 -1/3]\n [-1 -1 1] [ 1/3 1/3 1/3]' """ from sage.typeset.ascii_art import ascii_art from sage.matrix.constructor import matrix s = ascii_art('Double description pair (A, R) defined by') A = ascii_art(matrix(self.A)) A._baseline = (len(self.A) / 2) A = ascii_art('A = ') + A R = ascii_art(matrix(self.R).transpose()) if len(self.R) > 0: R._baseline = (len(self.R[0]) / 2) else: R._baseline = 0 R = ascii_art('R = ') + R return str(s * (A + ascii_art(', ') + R))
def bigraphical(self, G, A=None, K=QQ, names=None): r""" Return a bigraphical hyperplane arrangement. INPUT: - ``G`` -- graph - ``A`` -- list, matrix, dictionary (default: ``None`` gives semiorder), or the string 'generic' - ``K`` -- field (default: `\QQ`) - ``names`` -- tuple of strings or ``None`` (default); the variable names for the ambient space OUTPUT: The hyperplane arrangement with hyperplanes `x_i - x_j = A[i,j]` and `x_j - x_i = A[j,i]` for each edge `v_i, v_j` of ``G``. The indices `i,j` are the indices of elements of ``G.vertices()``. EXAMPLES:: sage: G = graphs.CycleGraph(4) sage: G.edges() [(0, 1, None), (0, 3, None), (1, 2, None), (2, 3, None)] sage: G.edges(labels=False) [(0, 1), (0, 3), (1, 2), (2, 3)] sage: A = {0:{1:1, 3:2}, 1:{0:3, 2:0}, 2:{1:2, 3:1}, 3:{2:0, 0:2}} sage: HA = hyperplane_arrangements.bigraphical(G, A) sage: HA.n_regions() 63 sage: hyperplane_arrangements.bigraphical(G, 'generic').n_regions() 65 sage: hyperplane_arrangements.bigraphical(G).n_regions() 59 REFERENCES: .. [BigraphicalArrangements] S. Hopkins, D. Perkinson. "Bigraphical Arrangements". :arxiv:`1212.4398` """ n = G.num_verts() if A is None: # default to G-semiorder arrangement A = matrix(K, n, lambda i, j: 1) elif A == 'generic': A = random_matrix(ZZ, n, x=10000) A = matrix(K, A) H = make_parent(K, n, names) x = H.gens() hyperplanes = [] for e in G.edges(): i = G.vertices().index(e[0]) j = G.vertices().index(e[1]) hyperplanes.append( x[i] - x[j] - A[i][j]) hyperplanes.append(-x[i] + x[j] - A[j][i]) return H(*hyperplanes)
def __init__(self, R, elements): """ Initialize ``self``. EXAMPLES:: sage: R.<x,y,z> = QQ[] sage: K = KoszulComplex(R, [x,y]) sage: TestSuite(K).run() """ # Generate the differentials self._elements = elements n = len(elements) I = range(n) diff = {} zero = R.zero() for i in I: M = matrix(R, binomial(n,i), binomial(n,i+1), zero) j = 0 for comb in itertools.combinations(I, i+1): for k,val in enumerate(comb): r = rank(comb[:k] + comb[k+1:], n, False) M[r,j] = (-1)**k * elements[val] j += 1 M.set_immutable() diff[i+1] = M diff[0] = matrix(R, 0, 1, zero) diff[0].set_immutable() diff[n+1] = matrix(R, 1, 0, zero) diff[n+1].set_immutable() ChainComplex_class.__init__(self, ZZ, ZZ(-1), R, diff)
def find_kadziela_matrices(M,T): ''' The matrix M describes the relation between periods (A,B,D)^t and the periods (A0,B0)^t, where (A,B,D) are the periods of the Teitelbaum periods, and (A0,B0) are the Darmon ones. (A,B,D)^t = M * (A0,B0)^t The matrix T describes the action of Hecke on homology. That is, the first column of T describes the image of T on the first basis vector. The output are matrices X and Y such that X * matrix(2,2,[A,B,B,D]) = matrix(2,2,[A0,B0,C0,D0]) * Y ''' a, b, c, d, e, f = M.list() x, y, z, t = T.list() # 1, 2, 3, 4, 5, 6, 7, 8 r1 = [a, c, 0, 0, -1, 0, 0, 0] r2 = [b, d, 0, 0, 0, 0, -1, 0] r3 = [c, e, 0, 0, 0, -1, 0, 0] r4 = [d, f, 0, 0, 0, 0, 0, -1] r5 = [0, 0, a, c, 0, 0, -1, 0] r6 = [0, 0,y*b,y*d, -z, 0,x-t, 0] r7 = [0, 0, c, e, 0, 0, 0, -1] r8 = [0, 0,y*d,y*f, 0, -z, 0,x-t] AA = matrix(ZZ,8,8,[r1,r2,r3,r4,r5,r6,r7,r8]) if AA.rank() == 8: raise ValueError('Not isogenous') r = AA.right_kernel().matrix().rows()[0].list() X = matrix(ZZ,2,2,r[:4]) Y = matrix(ZZ,2,2,r[4:]) return X, Y
def symmetric_matrix(self): r""" The symmetric matrix `M` such that `(x y z) M (x y z)^t` is the defining equation of ``self``. EXAMPLES :: sage: R.<x, y, z> = QQ[] sage: C = Conic(x^2 + x*y/2 + y^2 + z^2) sage: C.symmetric_matrix() [ 1 1/4 0] [1/4 1 0] [ 0 0 1] sage: C = Conic(x^2 + 2*x*y + y^2 + 3*x*z + z^2) sage: v = vector([x, y, z]) sage: v * C.symmetric_matrix() * v x^2 + 2*x*y + y^2 + 3*x*z + z^2 """ [a,b,c,d,e,f] = self.coefficients() if self.base_ring().characteristic() == 2: if b == 0 and c == 0 and e == 0: return matrix([[a,0,0],[0,d,0],[0,0,f]]) raise ValueError, "The conic self (= %s) has no symmetric matrix " \ "because the base field has characteristic 2" % \ self from sage.matrix.constructor import matrix return matrix([[ a , b/2, c/2 ], [ b/2, d , e/2 ], [ c/2, e/2, f ]])
def __call__(self, n, modulus=0): """ Give the nth term of a binary recurrence sequence, possibly mod some modulus. INPUT: - ``n`` -- an integer (the index of the term in the binary recurrence sequence) - ``modulus`` -- a natural number (optional -- default value is 0) OUTPUT: - An integer (the nth term of the binary recurrence sequence modulo ``modulus``) EXAMPLES:: sage: R = BinaryRecurrenceSequence(3,3,2,1) sage: R(2) 9 sage: R(101) 16158686318788579168659644539538474790082623100896663971001 sage: R(101,12) 9 sage: R(101)%12 9 """ R = Integers(modulus) F = matrix(R, [[0,1],[self.c,self.b]]) # F*[u_{n}, u_{n+1}]^T = [u_{n+1}, u_{n+2}]^T (T indicates transpose). v = matrix(R, [[self.u0],[self.u1]]) return list(F**n*v)[0][0]
def read_matrix(self, filename): r""" Read a matrix in 4ti2 format from the file ``filename`` in directory ``directory()``. INPUT: - ``filename`` - The name of the file to read from. OUTPUT: The data from the file as a matrix over `\ZZ`. EXAMPLES:: sage: from sage.interfaces.four_ti_2 import four_ti_2 sage: four_ti_2.write_matrix([[1,2,3],[3,4,6]], "test_file") sage: four_ti_2.read_matrix("test_file") [1 2 3] [3 4 6] """ from sage.matrix.constructor import matrix try: f = open(os.path.join(self.directory(), filename)) lines = f.readlines() f.close() except IOError: return matrix(ZZ, 0, 0) nrows, ncols = map(ZZ, lines.pop(0).strip().split()) return matrix(ZZ, nrows, ncols, [map(ZZ, line.strip().split()) for line in lines if line.strip() != ""])
def __init__(self, n): r""" Hecke triangle group (2, n, infinity). Namely the von Dyck group corresponding to the triangle group with angles (pi/2, pi/n, 0). INPUT: - ``n`` - ``infinity`` or an integer greater or equal to ``3``. OUTPUT: The Hecke triangle group for the given parameter ``n``. EXAMPLES:: sage: G = HeckeTriangleGroup(12) sage: G Hecke triangle group for n = 12 sage: G.category() Category of groups """ self._n = n self._T = matrix(AA, [[1,self.lam()],[0,1]]) self._S = matrix(AA, [[0,-1],[1,0]]) FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(2), AA, [self._S, self._T])
def Reverse(self): A1 = matrix(3, [1,1,1, 0,1,0, 0,0,1]) A2 = matrix(3, [1,0,0, 1,1,1, 0,0,1]) A3 = matrix(3, [1,0,0, 0,1,0, 1,1,1]) R = matrix(3, [0,1,1, 1,0,1, 1,1,0]) gens = {1:A1, 2:A2, 3:A3, 4:R} return MatrixCocycle(gens)
def Cassaigne_accelerated(self, order=3): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: c = cocycles.Cassaigne_accelerated(order=3) sage: c Cocycle with 6 gens over Language of finite words over alphabet ['11', '121', '12^{2}1', '212', '21^{2}2', '22'] """ C1 = matrix(3, [1,1,0, 0,0,1, 0,1,0]) C2 = matrix(3, [0,1,0, 1,0,0, 0,1,1]) C = {1:C1, 2:C2} gens = {} for i in range(order): for (a,b) in [(1,2), (2,1)]: if i == 0: code = '{}{}'.format(a,a) elif i == 1: code = '{}{}{}'.format(a,b,a) else: code = '{}{}^{{{}}}{}'.format(a,b,i,a) gens[code] = C[a]*C[b]**i*C[a] return MatrixCocycle(gens)
def random_isometry(self, preserve_orientation=True, **kwargs): r""" Return a random isometry in the Upper Half Plane model. INPUT: - ``preserve_orientation`` -- if ``True`` return an orientation-preserving isometry OUTPUT: - a hyperbolic isometry EXAMPLES:: sage: A = HyperbolicPlane().UHP().random_isometry() sage: B = HyperbolicPlane().UHP().random_isometry(preserve_orientation=False) sage: B.preserves_orientation() False """ [a,b,c,d] = [RR.random_element() for k in range(4)] while abs(a*d - b*c) < EPSILON: [a,b,c,d] = [RR.random_element() for k in range(4)] M = matrix(RDF, 2,[a,b,c,d]) M = M / (M.det()).abs().sqrt() if M.det() > 0: if not preserve_orientation: M = M * matrix(2,[0,1,1,0]) elif preserve_orientation: M = M * matrix(2,[0,1,1,0]) return self._Isometry(self, M, check=False)
def FullySubtractive(self): F1 = matrix(3, [1,0,0, 1,1,0, 1,0,1]) F2 = matrix(3, [1,1,0, 0,1,0, 0,1,1]) F3 = matrix(3, [1,0,1, 0,1,1, 0,0,1]) gens = (F1, F2, F3) alphabet = [1, 2, 3] gens = dict(zip(alphabet, gens)) return MatrixCocycle(gens)
def hom( self, im_gens, codomain=None, check=True): # return self.module().hom( im_gens, codomain = codomain, check = check) if not codomain: raise NotImplementedError() A = matrix( im_gens) if codomain and True == check: assert self.gram_matrix() == A*codomain.gram_matrix()*A.transpose() return Embedding( self, matrix( im_gens), codomain)
def ArnouxRauzy(self): A1 = matrix(3, [1,1,1, 0,1,0, 0,0,1]) A2 = matrix(3, [1,0,0, 1,1,1, 0,0,1]) A3 = matrix(3, [1,0,0, 0,1,0, 1,1,1]) gens = (A1, A2, A3) alphabet = [1, 2, 3] gens = dict(zip(alphabet, gens)) return MatrixCocycle(gens)
def Sorted_Brun(self): B1 = matrix(3, [1,0,0, 0,1,0, 0,1,1]) B2 = matrix(3, [1,0,0, 0,0,1, 0,1,1]) B3 = matrix(3, [0,1,0, 0,0,1, 1,0,1]) gens = (B1, B2, B3) alphabet = [1, 2, 3] gens = dict(zip(alphabet, gens)) cone = matrix(3, [1,1,1,0,1,1,0,0,1]) return MatrixCocycle(gens, cone)
def cartan_matrix(self, q = None, idempotents = None): """ EXAMPLES:: sage: import sage_semigroups.monoids.catalog as semigroups sage: M = semigroups.NDPFMonoidPoset(Posets(3)[3]) sage: M.cartan_matrix(var('q')) [1 0 0 0] [0 1 q 0] [0 0 1 0] [0 0 0 1] The algorithm used for computing the q-Cartan matrix is experimental. It has been tested for the 0-Hecke monoid in types A1-A4, B2-3,G2, and for NDPFMonoidB 1-5. sage: M = PiMonoid(["A",3]) # long time sage: m1 = M.cartan_matrix(var('q')) # long time sage: m2 = M.cartan_matrix_mupad(var('q')) # long time sage: isomorphic_cartan_matrices(m1,m2) # long time True sage: M = PiMonoid(["A",4]) # long time sage: m1 = M.cartan_matrix(var('q')) # long time sage: m2 = M.cartan_matrix_mupad(var('q')) # long time sage: isomorphic_cartan_matrices(m1,m2) # long time True The current version *does* fail for the 0-Hecke monoid of type D_4!!! sage: ZZ.<q> = ZZ[] sage: list(sum(sum(PiMonoid(["D",4]).cartan_matrix(q)))) # long time [16, 38, 62, 35, 20, 15, 6] Compare with: sage: list(sum(sum(PiMonoid(["D",4]).cartan_matrix_mupad(q)))) # long time [16, 38, 62, 38, 20, 12, 6] Which could mean that the factorization constraints are too weak. """ #left_symbols, right_modules = cartan_data_of_j_trivial_monoid(self, side="left" ) #right_symbols, left_modules = cartan_data_of_j_trivial_monoid(self, side="right") if idempotents is None: idempotents = self.idempotents() #idempotents = [self.retract(self.ambient.e(w)) for w in self.ambient.W] rank = rank_from_list(idempotents) from sage.matrix.constructor import matrix if q is None: cartan_matrix = matrix(len(idempotents), len(idempotents), sparse=True) else: cartan_matrix = matrix(q.parent(), len(idempotents), len(idempotents), sparse=True) for (e,f),coeff in self.cartan_matrix_as_table(q).iteritems(): cartan_matrix[rank(e), rank(f)] = coeff return cartan_matrix
def qlogs_from_Lp_and_ords(a,b,Tmatrix,q1ord, q2ord, q3ord): K = a.parent() ordA = q2ord + q3ord ordB = -q3ord ordD = q1ord + q3ord bord = matrix(K,2,2,[ordA,ordB,ordB,ordD]) * Tmatrix M = Matrix(K,3,2,[ordA, bord[0,0], ordB, bord[0,1],ordD,bord[1,1]]) logA, logB, logD = (M * matrix(K,2,1,[a,b])).list() return logB + logD, logA + logB, -logB
def Pigs(): r""" Return a Pigs game. Consider two pigs. One dominant pig and one subservient pig. These pigs share a pen. There is a lever in the pen that delivers 6 units of food but if either pig pushes the lever it will take them a little while to get to the food as well as cost them 1 unit of food. If the dominant pig pushes the lever, the subservient pig has some time to eat two thirds of the food before being pushed out of the way. If the subservient pig pushes the lever, the dominant pig will eat all the food. Finally if both pigs go to push the lever the subservient pig will be able to eat a third of the food (and they will also both lose 1 unit of food). This can be modeled as a normal form game using the following two matrices [McMillan]_ (we assume that the dominant pig's utilities are given by `A`): .. MATH:: A = \begin{pmatrix} 3&1\\ 6&0\\ \end{pmatrix} B = \begin{pmatrix} 1&4\\ -1&0\\ \end{pmatrix} There is a single Nash equilibrium at which the dominant pig pushes the lever and the subservient pig does not. This can be implemented in Sage using the following:: sage: g = game_theory.normal_form_games.Pigs() sage: g Pigs - Normal Form Game with the following utilities: ... sage: d ={(0, 1): [1, 4], (1, 0): [6, -1], ....: (0, 0): [3, 1], (1, 1): [0, 0]} sage: g == d True sage: g.obtain_nash() [[(1, 0), (0, 1)]] """ from sage.matrix.constructor import matrix A = matrix([[3, 1], [6, 0]]) B = matrix([[1, 4], [-1, 0]]) g = NormalFormGame([A, B]) g.rename('Pigs - ' + repr(g)) return g
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 relation_space(v): r""" Relation space of the given vector ``v`` This is the sub vector space of `\QQ^d` given as the kernel of the map `n \mapsto n \cdot \lambda`. The dimension is `d - rank`. EXAMPLES:: sage: from surface_dynamics.misc.linalg import relation_space sage: K.<sqrt2> = QuadraticField(2) sage: v3 = vector([sqrt2, 1, 1+sqrt2]) sage: relation_space(v3) Vector space of degree 3 and dimension 1 over Rational Field Basis matrix: [ 1 1 -1] sage: v4 = vector([sqrt2, 1, 1+sqrt2, 1-sqrt2]) sage: relation_space(v4) Vector space of degree 4 and dimension 2 over Rational Field Basis matrix: [ 1 0 -1/2 1/2] [ 0 1 -1/2 -1/2] sage: v = vector([1,2,5,3]) sage: relation_space(v) Vector space of degree 4 and dimension 3 over Rational Field Basis matrix: [ 1 0 0 -1/3] [ 0 1 0 -2/3] [ 0 0 1 -5/3] The relation space has some covariance relation with respect to matrix actions:: sage: m3 = matrix(3, [1,-1,0,2,-3,4,5,-2,2]) sage: relation_space(v3 * m3) == relation_space(v3) * ~m3.transpose() True sage: relation_space(m3 * v3) == relation_space(v3) * ~m3 True sage: m4 = matrix(4, [1,-1,0,1,2,-3,0,4,5,3,-2,2,1,1,1,1]) sage: relation_space(v4 * m4) == relation_space(v4) * ~m4.transpose() True sage: relation_space(m4 * v4) == relation_space(v4) * ~m4 True """ from sage.matrix.constructor import matrix try: m_lengths = matrix([u.vector() for u in v]) except AttributeError: from sage.rings.rational_field import QQ v = [QQ.coerce(i) for i in v] m_lengths = matrix([[i] for i in v]) return m_lengths.left_kernel()
def deformation_space(lengths): r""" Deformation space of the given ``lengths`` This is the smallest vector space defined over `\QQ` that contains the vector ``lengths``. Its dimension is `rank`. EXAMPLES:: sage: from surface_dynamics.misc.linalg import deformation_space sage: K.<sqrt2> = QuadraticField(2) sage: v3 = vector([sqrt2, 1, 1+sqrt2]) sage: deformation_space(v3) Vector space of degree 3 and dimension 2 over Rational Field Basis matrix: [1 0 1] [0 1 1] sage: v4 = vector([sqrt2, 1, 1+sqrt2, 1-sqrt2]) sage: deformation_space(v4) Vector space of degree 4 and dimension 2 over Rational Field Basis matrix: [ 1 0 1 -1] [ 0 1 1 1] sage: v = vector([1, 5, 2, 9]) sage: deformation_space(v) Vector space of degree 4 and dimension 1 over Rational Field Basis matrix: [1 5 2 9] The deformation space has some covariance relation with respect to matrix actions:: sage: m3 = matrix(3, [1,-1,0,2,-3,4,5,-2,2]) sage: deformation_space(v3 * m3) == deformation_space(v3) * m3 True sage: deformation_space(m3 * v3) == deformation_space(v3) * m3.transpose() True sage: m4 = matrix(4, [1,-1,0,1,2,-3,0,4,5,3,-2,2,1,1,1,1]) sage: deformation_space(v4 * m4) == deformation_space(v4) * m4 True sage: deformation_space(m4 * v4) == deformation_space(v4) * m4.transpose() True """ from sage.matrix.constructor import matrix try: m_lengths = matrix([u.vector() for u in lengths]) except AttributeError: from sage.rings.rational_field import QQ lengths = [QQ.coerce(i) for i in lengths] m_lengths = matrix([[i] for i in lengths]) return m_lengths.column_space()
def p_minimal_polynomials(self, p, s_max=None): r""" Compute `(p^s)`-minimal polynomials `\nu_s` of `B`. Compute a finite subset `\mathcal{S}` of the positive integers and `(p^s)`-minimal polynomials `\nu_s` for `s \in \mathcal{S}`. For `0 < t \le \max \mathcal{S}`, a `(p^t)`-minimal polynomial is given by `\nu_s` where `s = \min\{ r \in \mathcal{S} \mid r\ge t \}`. For `t > \max \mathcal{S}`, the minimal polynomial of `B` is also a `(p^t)`-minimal polynomial. INPUT: - ``p`` -- a prime in `D` - ``s_max`` -- a positive integer (default: ``None``); if set, only `(p^s)`-minimal polynomials for ``s <= s_max`` are computed (see below for details) OUTPUT: A dictionary. Keys are the finite set `\mathcal{S}`, the values are the associated `(p^s)`-minimal polynomials `\nu_s`, `s \in \mathcal{S}`. Setting ``s_max`` only affects the output if ``s_max`` is at most `\max\mathcal{S}` where `\mathcal{S}` denotes the full set. In that case, only those `\nu_s` with ``s <= s_max`` are returned where ``s_max`` is always included even if it is not included in the full set `\mathcal{S}`. EXAMPLES:: sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials sage: B = matrix(ZZ, [[1, 0, 1], [1, -2, -1], [10, 0, 0]]) sage: C = ComputeMinimalPolynomials(B) sage: C.p_minimal_polynomials(2) {2: x^2 + 3*x + 2} sage: set_verbose(1) sage: C = ComputeMinimalPolynomials(B) sage: C.p_minimal_polynomials(2) verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 1: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + x] [ x] [ 0] [ 1] [ 1] [ x + 1] [ 1] [ 0] [ 0] [ x + 1] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (x^2 + x) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) [x^2 + x] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^2 + x verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) corresponding columns for G verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + x] [ x + 2] [ 0] [ 1] [ 1] [ x - 1] [ -1] [ 10] [ 0] [ x + 1] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 2: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [ 2*x^2 + 2*x x^2 + 3*x + 2] [ 2*x x + 4] [ 0 0] [ 2 1] [ 2 1] [ 2*x + 2 x + 1] [ 2 -1] [ 0 10] [ 0 0] [ 2*x + 2 x + 3] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (2*x^2 + 2*x, x^2 + 3*x + 2) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) [x^2 + 3*x + 2] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^2 + 3*x + 2 verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) corresponding columns for G verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + 3*x + 2] [ x + 4] [ 0] [ 1] [ 1] [ x + 1] [ -1] [ 10] [ 0] [ x + 3] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 3: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^3 + 7*x^2 + 6*x x^3 + 3*x^2 + 2*x] [ x^2 + 8*x x^2 + 4*x] [ 0 0] [ x x + 4] [ x + 4 x] [ x^2 + 5*x + 4 x^2 + x] [ -x + 4 -x] [ 10*x 10*x] [ 0 0] [ x^2 + 7*x x^2 + 3*x + 4] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) ... verbose 1 (...: compute_J_ideal.py, current_nu) [x^3 + 3*x^2 + 2*x] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^3 + 3*x^2 + 2*x {2: x^2 + 3*x + 2} sage: set_verbose(0) sage: C.p_minimal_polynomials(2, s_max=1) {1: x^2 + x} sage: C.p_minimal_polynomials(2, s_max=2) {2: x^2 + 3*x + 2} sage: C.p_minimal_polynomials(2, s_max=3) {2: x^2 + 3*x + 2} ALGORITHM: [HR2016]_, Algorithm 5. """ from sage.misc.misc import verbose from sage.rings.infinity import Infinity deg_mu = self.mu_B.degree() if s_max is None: s_max = Infinity if p in self._cache: (t, G, p_min_polys) = self._cache[p] if t < Infinity: nu = G[0][0] else: t = 0 p_min_polys = {} nu = self._DX(1) d = self._A.ncols() G = matrix(self._DX, d, 0) while t < s_max: deg_prev_nu = nu.degree() t += 1 verbose("------------------------------------------") verbose("p = %s, t = %s:" % (p, t)) verbose("Result of lifting:") verbose("F =") F = lifting(p, t, self._A, G) verbose(F) nu = self.current_nu(p, t, F[0], nu) verbose("nu = %s" % nu) if nu.degree() >= deg_mu: t = Infinity break if nu.degree() == deg_prev_nu: G = G.delete_columns([G.ncols() - 1]) del p_min_polys[t-1] column = self.mccoy_column(p, t, nu) verbose("corresponding columns for G") verbose(column) G = matrix.block([[p * G, column]]) p_min_polys[t] = nu self._cache[p] = (t, G, p_min_polys) if s_max < t: result = {r: polynomial for r, polynomial in p_min_polys.items() if r < s_max} next_t_candidates = list(r for r in p_min_polys if r >= s_max) if next_t_candidates: next_t = min(next_t_candidates) result.update({s_max: p_min_polys[next_t] % p**s_max}) return result return p_min_polys
def generator_matrix(self): r""" Return a generator matrix of ``self`` Generator matrices of all Golay codes are known, and are thus returned by this method without performing any computation EXAMPLES:: sage: C = codes.GolayCode(GF(2), extended=True) sage: C.generator_matrix() [1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1] [0 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 1 0 0 1 0] [0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 1 0 1 1] [0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 0 1 1 0] [0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 1 0 1 1 0 0 1] [0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 1 0 1 1 0 1] [0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 1 0 1 1 1] [0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 1 1 1 1 0 0 0] [0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 1 1 1 1 0 0] [0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 1 1 1 1 0] [0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1 0 1] [0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1 1] """ n = self.length() if n == 23: G = matrix(GF(2), [[ 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1 ]]) elif n == 24: G = matrix(GF(2), [[ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1 ], [ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0 ], [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1 ], [ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0 ], [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1 ], [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1 ], [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1 ]]) elif n == 11: G = matrix(GF(3), [[2, 0, 1, 2, 1, 1, 0, 0, 0, 0, 0], [0, 2, 0, 1, 2, 1, 1, 0, 0, 0, 0], [0, 0, 2, 0, 1, 2, 1, 1, 0, 0, 0], [0, 0, 0, 2, 0, 1, 2, 1, 1, 0, 0], [0, 0, 0, 0, 2, 0, 1, 2, 1, 1, 0], [0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 1]]) else: G = matrix(GF(3), [[1, 0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 2], [0, 1, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0], [0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1], [0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 2, 2], [0, 0, 0, 0, 1, 0, 2, 1, 2, 2, 0, 1], [0, 0, 0, 0, 0, 1, 0, 2, 1, 2, 2, 1]]) return G
def insert_row(M, k, row): return matrix(M.rows()[:k] + [row] + M.rows()[k:])
def _find_isomorphism_degenerate(self, polytope): """ Helper to pick an isomorphism of degenerate polygons INPUT: - ``polytope`` -- a :class:`LatticePolytope_PPL_class`. The polytope to compare with. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL, C_Polyhedron sage: L1 = LatticePolytope_PPL(C_Polyhedron(2, 'empty')) sage: L2 = LatticePolytope_PPL(C_Polyhedron(3, 'empty')) sage: iso = L1.find_isomorphism(L2) # indirect doctest sage: iso(L1) == L2 True sage: iso = L1._find_isomorphism_degenerate(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,4)) sage: L2 = LatticePolytope_PPL((2,1,5)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,), (3,)) sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,-1), (3,-1)) sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,2), (3,1)) sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,2), (3,2)) sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4)) sage: L1.find_isomorphism(L2) Traceback (most recent call last): ... LatticePolytopesNotIsomorphicError: different number of integral points sage: L1 = LatticePolytope_PPL((-1,2), (3,1)) sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,5)) sage: L1.find_isomorphism(L2) Traceback (most recent call last): ... LatticePolytopesNotIsomorphicError: different number of integral points """ from sage.geometry.polyhedron.lattice_euclidean_group_element import \ LatticePolytopesNotIsomorphicError polytope_vertices = polytope.vertices() self_vertices = self.ordered_vertices() # handle degenerate cases if self.n_vertices() == 0: A = zero_matrix(ZZ, polytope.space_dimension(), self.space_dimension()) b = zero_vector(ZZ, polytope.space_dimension()) return LatticeEuclideanGroupElement(A, b) if self.n_vertices() == 1: A = zero_matrix(ZZ, polytope.space_dimension(), self.space_dimension()) b = polytope_vertices[0] return LatticeEuclideanGroupElement(A, b) if self.n_vertices() == 2: self_origin = self_vertices[0] self_ray = self_vertices[1] - self_origin polytope_origin = polytope_vertices[0] polytope_ray = polytope_vertices[1] - polytope_origin Ds, Us, Vs = self_ray.column().smith_form() Dp, Up, Vp = polytope_ray.column().smith_form() assert Vs.nrows() == Vs.ncols() == Vp.nrows() == Vp.ncols() == 1 assert abs(Vs[0, 0]) == abs(Vp[0, 0]) == 1 A = zero_matrix(ZZ, Dp.nrows(), Ds.nrows()) A[0, 0] = 1 A = Up.inverse() * A * Us * (Vs[0, 0] * Vp[0, 0]) b = polytope_origin - A * self_origin try: A = matrix(ZZ, A) b = vector(ZZ, b) except TypeError: raise LatticePolytopesNotIsomorphicError('different lattice') hom = LatticeEuclideanGroupElement(A, b) if hom(self) == polytope: return hom raise LatticePolytopesNotIsomorphicError('different polygons')
def dodecahedron(center=(0, 0, 0), size=1, **kwds): r""" A dodecahedron. INPUT: - ``center`` - (default: (0,0,0)) - ``size`` - (default: 1) - ``color`` - a string that describes a color; this can also be a list of 3-tuples or strings length 6 or 3, in which case the faces (and oppositive faces) are colored. - ``opacity`` - (default: 1) if less than 1 then is transparent EXAMPLES: A plain Dodecahedron:: sage: dodecahedron() A translucent dodecahedron that contains a black sphere:: sage: dodecahedron(color='orange', opacity=0.8) + \ sphere(size=0.5, color='black') CONSTRUCTION: This is how we construct a dodecahedron. We let one point be `Q = (0,1,0)`. Now there are three points spaced equally on a circle around the north pole. The other requirement is that the angle between them be the angle of a pentagon, namely `3\pi/5`. This is enough to determine them. Placing one on the `xz`-plane we have. `P_1 = \left(t, 0, \sqrt{1-t^2}\right)` `P_2 = \left(-\frac{1}{2}t, \frac{\sqrt{3}}{2}t, \sqrt{1-t^2}\right)` `P_3 = \left(-\frac{1}{2}t, \frac{\sqrt{3}}{2}t, \sqrt{1-t^2}\right)` Solving `\frac{(P_1-Q) \cdot (P_2-Q)}{|P_1-Q||P_2-Q|} = \cos(3\pi/5)` we get `t = 2/3`. Now we have 6 points `R_1, ..., R_6` to close the three top pentagons. These can be found by mirroring `P_2` and `P_3` by the `yz`-plane and rotating around the `y`-axis by the angle `\theta` from `Q` to `P_1`. Note that `\cos(\theta) = t = 2/3` and so `\sin(\theta) = \sqrt{5}/3`. Rotation gives us the other four. Now we reflect through the origin for the bottom half. AUTHORS: - Robert Bradshaw, William Stein """ RR = RDF one = RR(1) sqrt3 = RR(3).sqrt() sqrt5 = RR(5).sqrt() R3 = RR**3 rot = matrix( RR, [[-one / 2, -sqrt3 / 2, 0], [sqrt3 / 2, -one / 2, 0], [0, 0, 1]]) rot2 = rot * rot # The top Q = R3([0, 0, 1]) # The first ring P1 = R3([2 * one / 3, 0, sqrt5 / 3]) # The second ring R1 = R3([sqrt5 / 3, 1 / sqrt3, one / 3]) R2 = R3([sqrt5 / 3, -1 / sqrt3, one / 3]) top = [ Q, P1, rot * P1, rot2 * P1, R1, rot * R2, rot * R1, rot2 * R2, rot2 * R1, R2 ] point_list = top + [-p for p in reversed(top)] top_faces = [[0, 1, 4, 5, 2], [0, 2, 6, 7, 3], [0, 3, 8, 9, 1], [1, 9, 13, 12, 4], [2, 5, 11, 10, 6], [3, 7, 15, 14, 8]] face_list = top_faces + [[19 - p for p in reversed(f)] for f in top_faces] if 'aspect_ratio' not in kwds: kwds['aspect_ratio'] = [1, 1, 1] return index_face_set(face_list, point_list, enclosed=True, center=center, size=size, **kwds)
def basis(self, reduce=True): r""" Produce a basis for the free abelian group of eta-products of level N (under multiplication), attempting to find basis vectors of the smallest possible degree. INPUT: - ``reduce`` - a boolean (default True) indicating whether or not to apply LLL-reduction to the calculated basis EXAMPLES:: sage: EtaGroup(5).basis() [Eta product of level 5 : (eta_1)^6 (eta_5)^-6] sage: EtaGroup(12).basis() [Eta product of level 12 : (eta_1)^2 (eta_2)^1 (eta_3)^2 (eta_4)^-1 (eta_6)^-7 (eta_12)^3, Eta product of level 12 : (eta_1)^-4 (eta_2)^2 (eta_3)^4 (eta_6)^-2, Eta product of level 12 : (eta_1)^-1 (eta_2)^3 (eta_3)^3 (eta_4)^-2 (eta_6)^-9 (eta_12)^6, Eta product of level 12 : (eta_1)^1 (eta_2)^-1 (eta_3)^-3 (eta_4)^-2 (eta_6)^7 (eta_12)^-2, Eta product of level 12 : (eta_1)^-6 (eta_2)^9 (eta_3)^2 (eta_4)^-3 (eta_6)^-3 (eta_12)^1] sage: EtaGroup(12).basis(reduce=False) # much bigger coefficients [Eta product of level 12 : (eta_2)^24 (eta_12)^-24, Eta product of level 12 : (eta_1)^-336 (eta_2)^576 (eta_3)^696 (eta_4)^-216 (eta_6)^-576 (eta_12)^-144, Eta product of level 12 : (eta_1)^-8 (eta_2)^-2 (eta_6)^2 (eta_12)^8, Eta product of level 12 : (eta_1)^1 (eta_2)^9 (eta_3)^13 (eta_4)^-4 (eta_6)^-15 (eta_12)^-4, Eta product of level 12 : (eta_1)^15 (eta_2)^-24 (eta_3)^-29 (eta_4)^9 (eta_6)^24 (eta_12)^5] ALGORITHM: An eta product of level `N` is uniquely determined by the integers `r_d` for `d | N` with `d < N`, since `\sum_{d | N} r_d = 0`. The valid `r_d` are those that satisfy two congruences modulo 24, and one congruence modulo 2 for every prime divisor of N. We beef up the congruences modulo 2 to congruences modulo 24 by multiplying by 12. To calculate the kernel of the ensuing map `\ZZ^m \to (\ZZ/24\ZZ)^n` we lift it arbitrarily to an integer matrix and calculate its Smith normal form. This gives a basis for the lattice. This lattice typically contains "large" elements, so by default we pass it to the reduce_basis() function which performs LLL-reduction to give a more manageable basis. """ N = self.level() divs = divisors(N)[:-1] s = len(divs) primedivs = prime_divisors(N) rows = [] for i in range(s): # generate a row of relation matrix row = [ Mod(divs[i], 24) - Mod(N, 24), Mod(N / divs[i], 24) - Mod(1, 24) ] for p in primedivs: row.append(Mod(12 * (N / divs[i]).valuation(p), 24)) rows.append(row) M = matrix(IntegerModRing(24), rows) Mlift = M.change_ring(ZZ) # now we compute elementary factors of Mlift S, U, V = Mlift.smith_form() good_vects = [] for i in range(U.nrows()): vect = U.row(i) nf = (i < S.ncols() and S[i, i]) or 0 good_vects.append((vect * 24 / gcd(nf, 24)).list()) for v in good_vects: v.append(-sum([r for r in v])) dicts = [] for v in good_vects: dicts.append({}) for i in range(s): dicts[-1][divs[i]] = v[i] dicts[-1][N] = v[-1] if reduce: return self.reduce_basis([self(d) for d in dicts]) else: return [self(d) for d in dicts]
def affine_minimal(vp, return_transformation=False, D=None, quick=False): r""" Determine if given map is affine minimal. Given vp a scheme morphisms on the projective line over the rationals, this procedure determines if `\phi` is minimal. In particular, it determines if the map is affine minimal, which is enough to decide if it is minimal or not. See Proposition 2.10 in [Bruin-Molnar]_. INPUT: - ``vp`` -- scheme morphism on the projective line. - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes. - ``return_transformation`` -- a boolean value, default value True. This signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model. default: False. - ``quick`` -- a boolean value. If true the algorithm terminates once algorithm determines F/G is not minimal, otherwise algorithm only terminates once a minimal model has been found. OUTPUT: - ``newvp`` -- scheme morphism on the projective line. - ``conj`` -- linear fractional transformation which conjugates ``vp`` to ``newvp``. EXAMPLES:: sage: PS.<X,Y> = ProjectiveSpace(QQ, 1) sage: H = Hom(PS,PS) sage: vp = H([X^2 + 9*Y^2, X*Y]) sage: from sage.schemes.projective.endPN_minimal_model import affine_minimal sage: affine_minimal(vp, True) ( Scheme endomorphism of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (X : Y) to (X^2 + Y^2 : X*Y) , [3 0] [0 1] ) """ BR = vp.domain().base_ring() conj = matrix(BR,2,2,1) flag = True d = vp.degree() vp.normalize_coordinates(); Affvp = vp.dehomogenize(1) R = Affvp.coordinate_ring() if R.is_field(): #want the polynomial ring not the fraction field R = R.ring() F = R(Affvp[0].numerator()) G = R(Affvp[0].denominator()) if R(G.degree()) == 0 or R(F.degree()) == 0: raise TypeError("affine minimality is only considered for maps not of the form f or 1/f for a polynomial f") z = F.parent().gen(0) minF,minG = F,G #If the valuation of a prime in the resultant is small enough, we can say the #map is affine minimal at that prime without using the local minimality loop. See #Theorem 3.2.2 in [Molnar, M.Sc. thesis] if d%2 == 0: g = d else: g = 2*d Res = vp.resultant(); #Some quantities needed for the local minimization loop, but we compute now #since the value is constant, so we do not wish to compute in every local loop. #See Theorem 3.3.3 in [Molnar, M.Sc thesis] H = F-z*minG d1 = F.degree() A = AffineSpace(BR,1,H.parent().variable_name()) end_ring = End(A) ubRes = end_ring([H/minG]).homogenize(1).resultant() #Set the primes to check minimality at, if not already prescribed if D is None: D = ZZ(Res).prime_divisors() #Check minimality at all primes in D. If D is all primes dividing #Res(minF/minG), this is enough to show whether minF/minG is minimal or not. See #Propositions 3.2.1 and 3.3.7 in [Molnar, M.Sc. thesis]. for p in D: while True: if Res.valuation(p) < g: #The model is minimal at p min = True else: #The model may not be minimal at p. newvp,conj = Min(vp,p,ubRes,conj) if newvp == vp: min = True else: vp = newvp Affvp = vp.dehomogenize(1) min = False if min: #The model is minimal at p break elif F == Affvp[0].numerator() and G == Affvp[0].denominator(): #The model is minimal at p break else: #The model is not minimal at p flag = False if quick: break if quick and not flag: break if quick: #only return whether the model is minimal return flag if return_transformation: return vp, conj return vp
def _helper_payley_matrix(n, zero_position=True): r""" Return the marix constructed in Lemma 1.19 page 291 of [SWW72]_. This function return a `n^2` matrix `M` whose rows/columns are indexed by the element of a finite field on `n` elements `x_1,...,x_n`. The value `M_{i,j}` is equal to `\chi(x_i-x_j)`. The elements `x_1,...,x_n` are ordered in such a way that the matrix (respectively, its submatrix obtained by removing first row and first column in the case ``zero_position=False``) is symmetric with respect to its second diagonal. The matrix is symmetric if `n=4k+1`, and skew-symmetric otherwise. INPUT: - ``n`` -- an odd prime power. - ``zero_position`` -- if it is true (default), place 0 of ``F_n`` in the middle, otherwise place it first. .. SEEALSO:: :func:`rshcd_from_close_prime_powers` EXAMPLE:: sage: from sage.combinat.matrices.hadamard_matrix import _helper_payley_matrix sage: _helper_payley_matrix(5) [ 0 1 -1 -1 1] [ 1 0 1 -1 -1] [-1 1 0 1 -1] [-1 -1 1 0 1] [ 1 -1 -1 1 0] TESTS:: sage: _helper_payley_matrix(11,zero_position=True) [ 0 -1 1 -1 -1 -1 1 1 1 -1 1] [ 1 0 -1 -1 1 -1 1 -1 1 1 -1] [-1 1 0 1 -1 -1 -1 -1 1 1 1] [ 1 1 -1 0 1 -1 -1 1 -1 -1 1] [ 1 -1 1 -1 0 1 -1 -1 -1 1 1] [ 1 1 1 1 -1 0 1 -1 -1 -1 -1] [-1 -1 1 1 1 -1 0 1 -1 1 -1] [-1 1 1 -1 1 1 -1 0 1 -1 -1] [-1 -1 -1 1 1 1 1 -1 0 -1 1] [ 1 -1 -1 1 -1 1 -1 1 1 0 -1] [-1 1 -1 -1 -1 1 1 1 -1 1 0] sage: _helper_payley_matrix(11,zero_position=False) [ 0 1 1 1 1 -1 1 -1 -1 -1 -1] [-1 0 -1 1 -1 -1 1 1 1 -1 1] [-1 1 0 -1 -1 1 1 -1 1 1 -1] [-1 -1 1 0 1 -1 -1 -1 1 1 1] [-1 1 1 -1 0 1 -1 1 -1 -1 1] [ 1 1 -1 1 -1 0 -1 -1 -1 1 1] [-1 -1 -1 1 1 1 0 1 -1 1 -1] [ 1 -1 1 1 -1 1 -1 0 1 -1 -1] [ 1 -1 -1 -1 1 1 1 -1 0 -1 1] [ 1 1 -1 -1 1 -1 -1 1 1 0 -1] [ 1 -1 1 -1 -1 -1 1 1 -1 1 0] """ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF K = GF(n, prefix='x') # Order the elements of K in K_list # so that K_list[i] = -K_list[n-i-1] K_pairs = set(frozenset([x, -x]) for x in K) K_pairs.discard(frozenset([0])) K_list = [None] * n if zero_position: zero_position = n // 2 shift = 0 else: shift = 1 for i, (x, y) in enumerate(K_pairs): K_list[i + shift] = x K_list[-i - 1] = y K_list[zero_position] = K(0) M = matrix(n, [[2 * ((x - y).is_square()) - 1 for x in K_list] for y in K_list]) M = M - I(n) assert (M * J(n)).is_zero() assert (M * M.transpose()) == n * I(n) - J(n) return M
def regular_symmetric_hadamard_matrix_with_constant_diagonal( n, e, existence=False): r""" Return a Regular Symmetric Hadamard Matrix with Constant Diagonal. A Hadamard matrix is said to be *regular* if its rows all sum to the same value. For `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if `M` is a regular symmetric Hadamard matrix with constant diagonal `\delta\in\{-1,+1\}` and row sums all equal to `\delta \epsilon \sqrt(n)`. For more information, see [HX10]_ or 10.5.1 in [BH12]_. For the case `n=324`, see :func:`RSHCD_324` and [CP16]_. INPUT: - ``n`` (integer) -- side of the matrix - ``e`` -- one of `-1` or `+1`, equal to the value of `\epsilon` EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,1) [ 1 1 1 -1] [ 1 1 -1 1] [ 1 -1 1 1] [-1 1 1 1] sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,-1) [ 1 -1 -1 -1] [-1 1 -1 -1] [-1 -1 1 -1] [-1 -1 -1 1] Other hardcoded values:: sage: for n,e in [(36,1),(36,-1),(100,1),(100,-1),(196, 1)]: ....: print regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e) 36 x 36 dense matrix over Integer Ring 36 x 36 dense matrix over Integer Ring 100 x 100 dense matrix over Integer Ring 100 x 100 dense matrix over Integer Ring 196 x 196 dense matrix over Integer Ring sage: for n,e in [(324,1),(324,-1)]: # not tested - long time, tested in RSHCD_324 ....: print regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e) # not tested - long time 324 x 324 dense matrix over Integer Ring 324 x 324 dense matrix over Integer Ring From two close prime powers:: sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(64,-1) 64 x 64 dense matrix over Integer Ring Recursive construction:: sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(144,-1) 144 x 144 dense matrix over Integer Ring REFERENCE: .. [BH12] A. Brouwer and W. Haemers, Spectra of graphs, Springer, 2012, http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf .. [HX10] W. Haemers and Q. Xiang, Strongly regular graphs with parameters `(4m^4,2m^4+m^2,m^4+m^2,m^4+m^2)` exist for all `m>1`, European Journal of Combinatorics, Volume 31, Issue 6, August 2010, Pages 1553-1559, http://dx.doi.org/10.1016/j.ejc.2009.07.009. """ if existence and (n, e) in _rshcd_cache: return _rshcd_cache[n, e] from sage.graphs.strongly_regular_db import strongly_regular_graph def true(): _rshcd_cache[n, e] = True return True M = None if abs(e) != 1: raise ValueError if n < 0: if existence: return False raise ValueError elif n == 4: if existence: return true() if e == 1: M = J(4) - 2 * matrix(4, [[int(i + j == 3) for i in range(4)] for j in range(4)]) else: M = -J(4) + 2 * I(4) elif n == 36: if existence: return true() if e == 1: M = strongly_regular_graph(36, 15, 6, 6).adjacency_matrix() M = J(36) - 2 * M else: M = strongly_regular_graph(36, 14, 4, 6).adjacency_matrix() M = -J(36) + 2 * M + 2 * I(36) elif n == 100: if existence: return true() if e == -1: M = strongly_regular_graph(100, 44, 18, 20).adjacency_matrix() M = 2 * M - J(100) + 2 * I(100) else: M = strongly_regular_graph(100, 45, 20, 20).adjacency_matrix() M = J(100) - 2 * M elif n == 196 and e == 1: if existence: return true() M = strongly_regular_graph(196, 91, 42, 42).adjacency_matrix() M = J(196) - 2 * M elif n == 324: if existence: return true() M = RSHCD_324(e) elif (e == 1 and n % 16 == 0 and is_square(n) and is_prime_power(sqrt(n) - 1) and is_prime_power(sqrt(n) + 1)): if existence: return true() M = -rshcd_from_close_prime_powers(int(sqrt(n))) # Recursive construction: the kronecker product of two RSHCD is a RSHCD else: from itertools import product for n1, e1 in product(divisors(n)[1:-1], [-1, 1]): e2 = e1 * e n2 = n // n1 if (regular_symmetric_hadamard_matrix_with_constant_diagonal( n1, e1, existence=True) and regular_symmetric_hadamard_matrix_with_constant_diagonal( n2, e2, existence=True)): if existence: return true() M1 = regular_symmetric_hadamard_matrix_with_constant_diagonal( n1, e1) M2 = regular_symmetric_hadamard_matrix_with_constant_diagonal( n2, e2) M = M1.tensor_product(M2) break if M is None: from sage.misc.unknown import Unknown _rshcd_cache[n, e] = Unknown if existence: return Unknown raise ValueError("I do not know how to build a {}-RSHCD".format( (n, e))) assert M * M.transpose() == n * I(n) assert set(map(sum, M)) == {e * sqrt(n)} return M
def hadamard_matrix(n, existence=False, check=True): r""" Tries to construct a Hadamard matrix using a combination of Paley and Sylvester constructions. INPUT: - ``n`` (integer) -- dimension of the matrix - ``existence`` (boolean) -- whether to build the matrix or merely query if a construction is available in Sage. When set to ``True``, the function returns: - ``True`` -- meaning that Sage knows how to build the matrix - ``Unknown`` -- meaning that Sage does not know how to build the matrix, although the matrix may exist (see :mod:`sage.misc.unknown`). - ``False`` -- meaning that the matrix does not exist. - ``check`` (boolean) -- whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: hadamard_matrix(12).det() 2985984 sage: 12^6 2985984 sage: hadamard_matrix(1) [1] sage: hadamard_matrix(2) [ 1 1] [ 1 -1] sage: hadamard_matrix(8) # random [ 1 1 1 1 1 1 1 1] [ 1 -1 1 -1 1 -1 1 -1] [ 1 1 -1 -1 1 1 -1 -1] [ 1 -1 -1 1 1 -1 -1 1] [ 1 1 1 1 -1 -1 -1 -1] [ 1 -1 1 -1 -1 1 -1 1] [ 1 1 -1 -1 -1 -1 1 1] [ 1 -1 -1 1 -1 1 1 -1] sage: hadamard_matrix(8).det() == 8^4 True We note that the method `hadamard_matrix()` returns a normalised Hadamard matrix (the entries in the first row and column are all +1) :: sage: hadamard_matrix(12) # random [ 1 1| 1 1| 1 1| 1 1| 1 1| 1 1] [ 1 -1|-1 1|-1 1|-1 1|-1 1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 -1| 1 1|-1 -1|-1 -1| 1 1] [ 1 1|-1 -1| 1 -1|-1 1|-1 1| 1 -1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 1| 1 -1| 1 1|-1 -1|-1 -1] [ 1 1| 1 -1|-1 -1| 1 -1|-1 1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1|-1 -1| 1 1| 1 -1| 1 1|-1 -1] [ 1 1|-1 1| 1 -1|-1 -1| 1 -1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1|-1 -1|-1 -1| 1 1| 1 -1| 1 1] [ 1 1|-1 1|-1 1| 1 -1|-1 -1| 1 -1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 1|-1 -1|-1 -1| 1 1| 1 -1] [ 1 1| 1 -1|-1 1|-1 1| 1 -1|-1 -1] TESTS:: sage: matrix.hadamard(10,existence=True) False sage: matrix.hadamard(12,existence=True) True sage: matrix.hadamard(92,existence=True) Unknown sage: matrix.hadamard(10) Traceback (most recent call last): ... ValueError: The Hadamard matrix of order 10 does not exist """ if not (n % 4 == 0) and (n > 2): if existence: return False raise ValueError("The Hadamard matrix of order %s does not exist" % n) if n == 2: if existence: return True M = matrix([[1, 1], [1, -1]]) elif n == 1: if existence: return True M = matrix([1]) elif is_prime_power(n // 2 - 1) and (n // 2 - 1) % 4 == 1: if existence: return True M = hadamard_matrix_paleyII(n) elif n == 4 or n % 8 == 0: if existence: return hadamard_matrix(n // 2, existence=True) had = hadamard_matrix(n // 2, check=False) chad1 = matrix([list(r) + list(r) for r in had.rows()]) mhad = (-1) * had R = len(had.rows()) chad2 = matrix( [list(had.rows()[i]) + list(mhad.rows()[i]) for i in range(R)]) M = chad1.stack(chad2) elif is_prime_power(n - 1) and (n - 1) % 4 == 3: if existence: return True M = hadamard_matrix_paleyI(n) else: if existence: return Unknown raise ValueError( "The Hadamard matrix of order %s is not yet implemented." % n) if check: assert is_hadamard_matrix(M, normalized=True) return M
def hadamard_matrix_paleyII(n): """ Implements the Paley type II construction. The Paley type II case corresponds to the case `p \cong 1 \mod{4}` for a prime `p` (see [Hora]_). EXAMPLES:: sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12).det() 2985984 sage: 12^6 2985984 We note that the method returns a normalised Hadamard matrix :: sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12) [ 1 1| 1 1| 1 1| 1 1| 1 1| 1 1] [ 1 -1|-1 1|-1 1|-1 1|-1 1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 -1| 1 1|-1 -1|-1 -1| 1 1] [ 1 1|-1 -1| 1 -1|-1 1|-1 1| 1 -1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 1| 1 -1| 1 1|-1 -1|-1 -1] [ 1 1| 1 -1|-1 -1| 1 -1|-1 1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1|-1 -1| 1 1| 1 -1| 1 1|-1 -1] [ 1 1|-1 1| 1 -1|-1 -1| 1 -1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1|-1 -1|-1 -1| 1 1| 1 -1| 1 1] [ 1 1|-1 1|-1 1| 1 -1|-1 -1| 1 -1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 1|-1 -1|-1 -1| 1 1| 1 -1] [ 1 1| 1 -1|-1 1|-1 1| 1 -1|-1 -1] TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import (hadamard_matrix_paleyII, is_hadamard_matrix) sage: test_cases = [2*(x+1) for x in range(50) if is_prime_power(x) and x%4==1] sage: all(is_hadamard_matrix(hadamard_matrix_paleyII(n),normalized=True,verbose=True) ....: for n in test_cases) True """ q = n // 2 - 1 if not (n % 2 == 0 and is_prime_power(q) and (q % 4 == 1)): raise ValueError( "The order %s is not covered by the Paley type II construction." % n) from sage.rings.finite_rings.finite_field_constructor import FiniteField K = FiniteField(q, 'x') K_list = list(K) K_list.insert(0, K.zero()) H = matrix(ZZ, [[(1 if (x - y).is_square() else -1) for x in K_list] for y in K_list]) for i in range(q + 1): H[0, i] = 1 H[i, 0] = 1 H[i, i] = 0 tr = { 0: matrix(2, 2, [1, -1, -1, -1]), 1: matrix(2, 2, [1, 1, 1, -1]), -1: matrix(2, 2, [-1, -1, -1, 1]) } H = block_matrix(q + 1, q + 1, [tr[v] for r in H for v in r]) return normalise_hadamard(H)
def skew_hadamard_matrix(n, existence=False, skew_normalize=True, check=True): r""" Tries to construct a skew Hadamard matrix A Hadamard matrix `H` is called skew if `H=S-I`, for `I` the identity matrix and `-S=S^\top`. Currently constructions from Section 14.1 of [Ha83]_ and few more exotic ones are implemented. INPUT: - ``n`` (integer) -- dimension of the matrix - ``existence`` (boolean) -- whether to build the matrix or merely query if a construction is available in Sage. When set to ``True``, the function returns: - ``True`` -- meaning that Sage knows how to build the matrix - ``Unknown`` -- meaning that Sage does not know how to build the matrix, but that the design may exist (see :mod:`sage.misc.unknown`). - ``False`` -- meaning that the matrix does not exist. - ``skew_normalize`` (boolean) -- whether to make the 1st row all-one, and adjust the 1st column accordingly. Set to ``True`` by default. - ``check`` (boolean) -- whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix sage: skew_hadamard_matrix(12).det() 2985984 sage: 12^6 2985984 sage: skew_hadamard_matrix(1) [1] sage: skew_hadamard_matrix(2) [ 1 1] [-1 1] TESTS:: sage: skew_hadamard_matrix(10,existence=True) False sage: skew_hadamard_matrix(12,existence=True) True sage: skew_hadamard_matrix(784,existence=True) True sage: skew_hadamard_matrix(10) Traceback (most recent call last): ... ValueError: A skew Hadamard matrix of order 10 does not exist sage: skew_hadamard_matrix(36) 36 x 36 dense matrix over Integer Ring... sage: skew_hadamard_matrix(36)==skew_hadamard_matrix(36,skew_normalize=False) False sage: skew_hadamard_matrix(52) 52 x 52 dense matrix over Integer Ring... sage: skew_hadamard_matrix(92) 92 x 92 dense matrix over Integer Ring... sage: skew_hadamard_matrix(816) # long time 816 x 816 dense matrix over Integer Ring... sage: skew_hadamard_matrix(100) Traceback (most recent call last): ... ValueError: A skew Hadamard matrix of order 100 is not yet implemented. sage: skew_hadamard_matrix(100,existence=True) Unknown REFERENCES: .. [Ha83] M. Hall, Combinatorial Theory, 2nd edition, Wiley, 1983 """ def true(): _skew_had_cache[n] = True return True M = None if existence and n in _skew_had_cache: return True if not (n % 4 == 0) and (n > 2): if existence: return False raise ValueError("A skew Hadamard matrix of order %s does not exist" % n) if n == 2: if existence: return true() M = matrix([[1, 1], [-1, 1]]) elif n == 1: if existence: return true() M = matrix([1]) elif is_prime_power(n - 1) and ((n - 1) % 4 == 3): if existence: return true() M = hadamard_matrix_paleyI(n, normalize=False) elif n % 8 == 0: if skew_hadamard_matrix(n // 2, existence=True): # (Lemma 14.1.6 in [Ha83]_) if existence: return true() H = skew_hadamard_matrix(n // 2, check=False) M = block_matrix([[H, H], [-H.T, H.T]]) else: # try Williamson construction (Lemma 14.1.5 in [Ha83]_) for d in divisors(n)[2:-2]: # skip 1, 2, n/2, and n n1 = n // d if is_prime_power(d - 1) and (d % 4 == 0) and (n1 % 4 == 0)\ and skew_hadamard_matrix(n1,existence=True): if existence: return true() H = skew_hadamard_matrix(n1, check=False) - I(n1) U = matrix(ZZ, d, lambda i, j: -1 if i==j==0 else\ 1 if i==j==1 or (i>1 and j-1==d-i)\ else 0) A = block_matrix( [[matrix([0]), matrix(ZZ, 1, d - 1, [1] * (d - 1))], [ matrix(ZZ, d - 1, 1, [-1] * (d - 1)), _helper_payley_matrix(d - 1, zero_position=0) ]]) + I(d) M = A.tensor_product(I(n1)) + (U * A).tensor_product(H) break if M is None: # try Williamson-Goethals-Seidel construction if GS_skew_hadamard_smallcases(n, existence=True): if existence: return true() M = GS_skew_hadamard_smallcases(n) else: if existence: return Unknown raise ValueError( "A skew Hadamard matrix of order %s is not yet implemented." % n) if skew_normalize: dd = diagonal_matrix(M[0]) M = dd * M * dd if check: assert is_hadamard_matrix(M, normalized=False, skew=True) if skew_normalize: from sage.modules.free_module_element import vector assert M[0] == vector([1] * n) _skew_had_cache[n] = True return M
def lifting(p, t, A, G): r""" Compute generators of `\{f \in D[X]^d \mid Af \equiv 0 \pmod{p^{t}}\}` given generators of `\{f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`. INPUT: - ``p`` -- a prime element of some principal ideal domain `D` - ``t`` -- a non-negative integer - ``A`` -- a `c\times d` matrix over `D[X]` - ``G`` -- a matrix over `D[X]`. The columns of `\begin{pmatrix}p^{t-1}I& G\end{pmatrix}` are generators of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`; can be set to ``None`` if ``t`` is zero OUTPUT: A matrix `F` over `D[X]` such that the columns of `\begin{pmatrix}p^tI&F&pG\end{pmatrix}` are generators of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^t}\}`. EXAMPLES:: sage: from sage.matrix.compute_J_ideal import lifting sage: X = polygen(ZZ, 'X') sage: A = matrix([[1, X], [2*X, X^2]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 [] sage: (A*G1 % 5).is_zero() True sage: A = matrix([[1, X, X^2], [2*X, X^2, 3*X^3]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 [3*X^2] [ X] [ 1] sage: (A*G1 % 5).is_zero() True sage: G2 = lifting(5, 2, A, G1); G2 [15*X^2 23*X^2] [ 5*X X] [ 5 1] sage: (A*G2 % 25).is_zero() True sage: lifting(5, 10, A, G1) Traceback (most recent call last): ... ValueError: A*G not zero mod 5^9 ALGORITHM: [HR2016]_, Algorithm 1. TESTS:: sage: A = matrix([[1, X], [X, X^2]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 Traceback (most recent call last): ... ValueError: [ 1 X|] [ X X^2|] does not have full rank. """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing DX = A.parent().base() (X,) = DX.variable_names() D = DX.base_ring() d = A.ncols() c = A.nrows() if t == 0: return matrix(DX, d, 0) if not (A*G % p**(t-1)).is_zero(): raise ValueError("A*G not zero mod %s^%s" % (p, t-1)) R = A*G/p**(t-1) R.change_ring(DX) AR = matrix.block([[A, R]]) Fp = D.quotient(p*D) FpX = PolynomialRing(Fp, name=X) ARb = AR.change_ring(FpX) (Db, Sb, Tb) = ARb.smith_form() #assert Sb * ARb * Tb == Db #assert all(i == j or Db[i, j].is_zero() # for i in range(Db.nrows()) # for j in range(Db.ncols())) r = Db.rank() if r != c: raise ValueError("{} does not have full rank.".format(ARb)) T = Tb.change_ring(DX) F1 = matrix.block([[p**(t-1) * matrix.identity(d), G]])*T F = F1.matrix_from_columns(range(r, F1.ncols())) assert (A*F % (p**t)).is_zero(), "A*F=%s" % (A*F) return F
def duality_pairing_matrix(self, basis, degree): r""" The matrix of scalar products between elements of NSym and elements of QSym. INPUT: - ``basis`` -- A basis of the dual Hopf algebra - ``degree`` -- a non-negative integer OUTPUT: - The matrix of scalar products between the basis ``self`` and the basis ``basis`` in the dual Hopf algebra of degree ``degree``. EXAMPLES: The ribbon basis of NCSF is dual to the fundamental basis of QSym:: sage: R = NonCommutativeSymmetricFunctions(QQ).ribbon() sage: F = QuasiSymmetricFunctions(QQ).Fundamental() sage: R.duality_pairing_matrix(F, 3) [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] sage: F.duality_pairing_matrix(R, 3) [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] The complete basis of NCSF is dual to the monomial basis of QSym:: sage: S = NonCommutativeSymmetricFunctions(QQ).complete() sage: M = QuasiSymmetricFunctions(QQ).Monomial() sage: S.duality_pairing_matrix(M, 3) [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] sage: M.duality_pairing_matrix(S, 3) [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] The matrix between the ribbon basis of NCSF and the monomial basis of QSym:: sage: R = NonCommutativeSymmetricFunctions(QQ).ribbon() sage: M = QuasiSymmetricFunctions(QQ).Monomial() sage: R.duality_pairing_matrix(M, 3) [ 1 -1 -1 1] [ 0 1 0 -1] [ 0 0 1 -1] [ 0 0 0 1] sage: M.duality_pairing_matrix(R, 3) [ 1 0 0 0] [-1 1 0 0] [-1 0 1 0] [ 1 -1 -1 1] The matrix between the complete basis of NCSF and the fundamental basis of QSym:: sage: S = NonCommutativeSymmetricFunctions(QQ).complete() sage: F = QuasiSymmetricFunctions(QQ).Fundamental() sage: S.duality_pairing_matrix(F, 3) [1 1 1 1] [0 1 0 1] [0 0 1 1] [0 0 0 1] A base case test:: sage: R.duality_pairing_matrix(M,0) [1] """ from sage.matrix.constructor import matrix # TODO: generalize to keys indexing the basis of the graded component from sage.combinat.composition import Compositions return matrix(self.base_ring(), [[self.duality_pairing(self[I], basis[J]) \ for J in Compositions(degree)] \ for I in Compositions(degree)])
def Min(Fun, p, ubRes, conj): r""" Local loop for Affine_minimal, where we check minimality at the prime p. First we bound the possible k in our transformations A = zp^k + b. See Theorems 3.3.2 and 3.3.3 in [Molnar]_. INPUT: - ``Fun`` -- a projective space morphisms. - ``p`` - a prime. - ``ubRes`` -- integer, the upper bound needed for Th. 3.3.3 in [Molnar]_. - ``conj`` -- a 2x2 matrix keeping track of the conjugation. OUTPUT: - Boolean -- ``True`` if ``Fun`` is minimal at ``p``, ``False`` otherwise. - a projective morphism minimal at ``p``. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: H = End(P) sage: f = H([149*x^2 + 39*x*y + y^2, -8*x^2 + 137*x*y + 33*y^2]) sage: from sage.schemes.projective.endPN_minimal_model import Min sage: Min(f, 3, -27000000, matrix(QQ,[[1, 0],[0, 1]])) ( Scheme endomorphism of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (181*x^2 + 313*x*y + 81*y^2 : -24*x^2 + 73*x*y + 151*y^2) , [3 4] [0 1] ) """ d = Fun.degree() AffFun = Fun.dehomogenize(1) R = AffFun.coordinate_ring() if R.is_field(): #want the polynomial ring not the fraction field R = R.ring() F = R(AffFun[0].numerator()) G = R(AffFun[0].denominator()) dG = G.degree() if dG > (d+1)/2: lowerBound = (-2*(G[dG]).valuation(p)/(2*dG - d + 1) + 1).floor() else: lowerBound = (-2*(F[d]).valuation(p)/(d-1) + 1).floor() upperBound = 2*(ubRes.valuation(p)) if upperBound < lowerBound: #There are no possible transformations to reduce the resultant. return Fun,conj else: #Looping over each possible k, we search for transformations to reduce the #resultant of F/G k = lowerBound Qb = PolynomialRing(QQ,'b') b = Qb.gen(0) Q = PolynomialRing(Qb,'z') z = Q.gen(0) while k <= upperBound: A = (p**k)*z + b Ft = Q(F(A) - b*G(A)) Gt = Q((p**k)*G(A)) Fcoeffs = Ft.coefficients(sparse=False) Gcoeffs = Gt.coefficients(sparse=False) coeffs = Fcoeffs + Gcoeffs RHS = (d + 1)*k/2 #If there is some b such that Res(phi^A) < Res(phi), we must have ord_p(c) > #RHS for each c in coeffs. #Make sure constant coefficients in coeffs satisfy the inequality. if all( QQ(c).valuation(p) > RHS for c in coeffs if c.degree() ==0 ): #Constant coefficients in coeffs have large enough valuation, so check #the rest. We start by checking if simply picking b=0 works if all(c(0).valuation(p) > RHS for c in coeffs): #A = z*p^k satisfies the inequalities, and F/G is not minimal #"Conjugating by", p,"^", k, "*z +", 0 newconj = matrix(QQ,2,2,[p**k,0,0,1]) minFun = Fun.conjugate(newconj) conj = conj*newconj minFun.normalize_coordinates() return minFun, conj #Otherwise we search if any value of b will work. We start by finding a #minimum bound on the valuation of b that is necessary. See Theorem 3.3.5 #in [Molnar, M.Sc. thesis]. bval = max([bCheck(coeff,RHS,p,b) for coeff in coeffs if coeff.degree() > 0]) #We scale the coefficients in coeffs, so that we may assume ord_p(b) is #at least 0 scaledCoeffs = [coeff(b*(p**bval)) for coeff in coeffs] #We now scale the inequalities, ord_p(coeff) > RHS, so that coeff is in #ZZ[b] scale = QQ(max([coeff.denominator() for coeff in scaledCoeffs])) normalizedCoeffs = [coeff*scale for coeff in scaledCoeffs] scaleRHS = RHS + scale.valuation(p) #We now search for integers that satisfy the inequality ord_p(coeff) > #RHS. See Lemma 3.3.6 in [Molnar, M.Sc. thesis]. bound = (scaleRHS+1).floor() bool,sol = blift(normalizedCoeffs,bound,p) #If bool is true after lifting, we have a solution b, and F/G is not #minimal. if bool: #Rescale, conjugate and return new map bsol = QQ(sol*(p**bval)) #"Conjugating by ", p,"^", k, "*z +", bsol newconj = matrix(QQ,2,2,[p**k,bsol,0,1]) minFun = Fun.conjugate(newconj) conj = conj*newconj minFun.normalize_coordinates() return minFun, conj k = k + 1 return Fun, conj
def rshcd_from_close_prime_powers(n): r""" Return a `(n^2,1)`-RSHCD when `n-1` and `n+1` are odd prime powers and `n=0\pmod{4}`. The construction implemented here appears in Theorem 4.3 from [GS70]_. Note that the authors of [SWW72]_ claim in Corollary 5.12 (page 342) to have proved the same result without the `n=0\pmod{4}` restriction with a *very* similar construction. So far, however, I (Nathann Cohen) have not been able to make it work. INPUT: - ``n`` -- an integer congruent to `0\pmod{4}` .. SEEALSO:: :func:`regular_symmetric_hadamard_matrix_with_constant_diagonal` EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import rshcd_from_close_prime_powers sage: rshcd_from_close_prime_powers(4) [-1 -1 1 -1 1 -1 -1 1 -1 1 -1 -1 1 -1 1 -1] [-1 -1 1 1 -1 -1 -1 -1 -1 1 1 -1 -1 1 -1 1] [ 1 1 -1 1 1 -1 -1 -1 -1 -1 1 -1 -1 -1 1 -1] [-1 1 1 -1 1 1 -1 -1 -1 -1 -1 1 -1 -1 -1 1] [ 1 -1 1 1 -1 1 1 -1 -1 -1 -1 -1 1 -1 -1 -1] [-1 -1 -1 1 1 -1 1 1 -1 -1 -1 1 -1 1 -1 -1] [-1 -1 -1 -1 1 1 -1 -1 1 -1 1 -1 1 1 -1 -1] [ 1 -1 -1 -1 -1 1 -1 -1 -1 1 -1 1 -1 1 1 -1] [-1 -1 -1 -1 -1 -1 1 -1 -1 -1 1 1 1 -1 1 1] [ 1 1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 1 1 -1 1] [-1 1 1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 1 1 -1] [-1 -1 -1 1 -1 1 -1 1 1 -1 -1 -1 -1 -1 1 1] [ 1 -1 -1 -1 1 -1 1 -1 1 1 -1 -1 -1 -1 -1 1] [-1 1 -1 -1 -1 1 1 1 -1 1 1 -1 -1 -1 -1 -1] [ 1 -1 1 -1 -1 -1 -1 1 1 -1 1 1 -1 -1 -1 -1] [-1 1 -1 1 -1 -1 -1 -1 1 1 -1 1 1 -1 -1 -1] REFERENCE: .. [SWW72] A Street, W. Wallis, J. Wallis, Combinatorics: Room squares, sum-free sets, Hadamard matrices. Lecture notes in Mathematics 292 (1972). """ if n % 4: raise ValueError("n(={}) must be congruent to 0 mod 4") a, b = sorted([n - 1, n + 1], key=lambda x: -x % 4) Sa = _helper_payley_matrix(a) Sb = _helper_payley_matrix(b) U = matrix(a, [[int(i + j == a - 1) for i in range(a)] for j in range(a)]) K = (U * Sa).tensor_product(Sb) + U.tensor_product(J(b) - I(b)) - J( a).tensor_product(I(b)) F = lambda x: diagonal_matrix([-(-1)**i for i in range(x)]) G = block_diagonal_matrix([J(1), I(a).tensor_product(F(b))]) e = matrix(a * b, [1] * (a * b)) H = block_matrix(2, [-J(1), e.transpose(), e, K]) HH = G * H * G assert len(set(map(sum, HH))) == 1 assert HH**2 == n**2 * I(n**2) return HH
def _LKB_matrix_(self, braid, variab): """ Compute the Lawrence-Krammer-Bigelow representation matrix. The variables of the matrix must be given. This actual computation is done in this helper method for caching purposes. INPUT: - ``braid`` -- tuple of integers. The Tietze list of the braid. - ``variab`` -- string. the names of the variables that will appear in the matrix. They must be given as a string, separated by a comma OUTPUT: The LKB matrix of the braid, with respect to the variables. TESTS:: sage: B=BraidGroup(3) sage: B._LKB_matrix_((2, 1, 2), 'x, y') [ 0 -x^4*y + x^3*y -x^4*y] [ 0 -x^3*y 0] [ -x^2*y x^3*y - x^2*y 0] sage: B._LKB_matrix_((1, 2, 1), 'x, y') [ 0 -x^4*y + x^3*y -x^4*y] [ 0 -x^3*y 0] [ -x^2*y x^3*y - x^2*y 0] sage: B._LKB_matrix_((-1, -2, -1, 2, 1, 2), 'x, y') [1 0 0] [0 1 0] [0 0 1] """ n = self.strands() if len(braid) > 1: A = self._LKB_matrix_(braid[:1], variab) for i in braid[1:]: A = A * self._LKB_matrix_((i, ), variab) return A l = list(Set(range(n)).subsets(2)) R = LaurentPolynomialRing(IntegerRing(), variab) q = R.gens()[0] t = R.gens()[1] if len(braid) == 0: return identity_matrix(R, len(l), sparse=True) A = matrix(R, len(l), sparse=True) if braid[0] > 0: i = braid[0] - 1 for m in range(len(l)): j = min(l[m]) k = max(l[m]) if i == j - 1: A[l.index(Set([i, k])), m] = q A[l.index(Set([i, j])), m] = q * q - q A[l.index(Set([j, k])), m] = 1 - q elif i == j and not j == k - 1: A[l.index(Set([j, k])), m] = 0 A[l.index(Set([j + 1, k])), m] = 1 elif k - 1 == i and not k - 1 == j: A[l.index(Set([j, i])), m] = q A[l.index(Set([j, k])), m] = 1 - q A[l.index(Set([i, k])), m] = (1 - q) * q * t elif i == k: A[l.index(Set([j, k])), m] = 0 A[l.index(Set([j, k + 1])), m] = 1 elif i == j and j == k - 1: A[l.index(Set([j, k])), m] = -t * q * q else: A[l.index(Set([j, k])), m] = 1 return A else: i = -braid[0] - 1 for m in range(len(l)): j = min(l[m]) k = max(l[m]) if i == j - 1: A[l.index(Set([j - 1, k])), m] = 1 elif i == j and not j == k - 1: A[l.index(Set([j + 1, k])), m] = q**(-1) A[l.index(Set([j, k])), m] = 1 - q**(-1) A[l.index(Set([j, j + 1])), m] = t**(-1) * q**(-1) - t**(-1) * q**(-2) elif k - 1 == i and not k - 1 == j: A[l.index(Set([j, k - 1])), m] = 1 elif i == k: A[l.index(Set([j, k + 1])), m] = q**(-1) A[l.index(Set([j, k])), m] = 1 - q**(-1) A[l.index(Set([k, k + 1])), m] = -q**(-1) + q**(-2) elif i == j and j == k - 1: A[l.index(Set([j, k])), m] = -t**(-1) * q**(-2) else: A[l.index(Set([j, k])), m] = 1 return A
def hadamard_matrix_paleyI(n, normalize=True): """ Implements the Paley type I construction. The Paley type I case corresponds to the case `p \cong 3 \mod{4}` for a prime `p` (see [Hora]_). INPUT: - ``n`` -- the matrix size - ``normalize`` (boolean) -- whether to normalize the result. EXAMPLES: We note that this method by default returns a normalised Hadamard matrix :: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_paleyI sage: hadamard_matrix_paleyI(4) [ 1 1 1 1] [ 1 -1 1 -1] [ 1 -1 -1 1] [ 1 1 -1 -1] Otherwise, it returns a skew Hadamard matrix `H`, i.e. `H=S+I`, with `S=-S^\top` :: sage: M=hadamard_matrix_paleyI(4, normalize=False); M [ 1 1 1 1] [-1 1 1 -1] [-1 -1 1 1] [-1 1 -1 1] sage: S=M-identity_matrix(4); -S==S.T True TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix sage: test_cases = [x+1 for x in range(100) if is_prime_power(x) and x%4==3] sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n),normalized=True,verbose=True) ....: for n in test_cases) True sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n,normalize=False),verbose=True) ....: for n in test_cases) True """ p = n - 1 if not (is_prime_power(p) and (p % 4 == 3)): raise ValueError( "The order %s is not covered by the Paley type I construction." % n) from sage.rings.finite_rings.finite_field_constructor import FiniteField K = FiniteField(p, 'x') K_list = list(K) K_list.insert(0, K.zero()) H = matrix(ZZ, [[(1 if (x - y).is_square() else -1) for x in K_list] for y in K_list]) for i in range(n): H[i, 0] = -1 H[0, i] = 1 if normalize: for i in range(n): H[i, i] = -1 H = normalise_hadamard(H) return H
def _eta_relations_helper(eta1, eta2, degree, qexp_terms, labels, verbose): r""" Helper function used by eta_poly_relations. Finds a basis for the space of linear relations between the first qexp_terms of the `q`-expansions of the monomials `\eta_1^i * \eta_2^j` for `0 \le i,j < degree`, and calculates a Groebner basis for the ideal generated by these relations. Liable to return meaningless results if qexp_terms isn't at least `1 + d*(m_1,m_2)` where .. MATH:: m_i = min(0, {\text degree of the pole of $\eta_i$ at $\infty$}) as then 1 will be in the ideal. EXAMPLES:: sage: from sage.modular.etaproducts import _eta_relations_helper sage: r,s = EtaGroup(4).basis() sage: _eta_relations_helper(r,s,4,100,['a','b'],False) [a*b - a + 16] sage: _eta_relations_helper(EtaProduct(26, {2:2,13:2,26:-2,1:-2}),EtaProduct(26, {2:4,13:2,26:-4,1:-2}),3,12,['a','b'],False) # not enough terms, will return rubbish [1] """ indices = [(i, j) for j in range(degree) for i in range(degree)] inf = CuspFamily(eta1.level(), 1) pole_at_infinity = -(min([0, eta1.order_at_cusp(inf)]) + min([0, eta2.order_at_cusp(inf)])) * degree if verbose: print("Trying all coefficients from q^%s to q^%s inclusive" % (-pole_at_infinity, -pole_at_infinity + qexp_terms - 1)) rows = [] for j in range(qexp_terms): rows.append([]) for i in indices: func = (eta1**i[0] * eta2**i[1]).qexp(qexp_terms) for j in range(qexp_terms): rows[j].append(func[j - pole_at_infinity]) M = matrix(rows) V = M.right_kernel() if V.dimension() == 0: if verbose: print("No polynomial relation of order %s valid for %s terms" % (degree, qexp_terms)) return None if V.dimension() >= 1: R = PolynomialRing(QQ, 2, labels) x, y = R.gens() relations = [] for c in V.basis(): relations.append( sum([ c[v] * x**indices[v][0] * y**indices[v][1] for v in range(len(indices)) ])) id = R.ideal(relations) return id.groebner_basis()
def hilbert_symbol_negative_at_S(self, S, b, check=True): r""" Returns an integer that has a negative Hilbert symbol with respect to a given rational number and a given set of primes (or places). The function is algorithm 3.4.1 in [Kir2016]_. It finds an integer `a` that has negative Hilbert symbol with respect to a given rational number exactly at a given set of primes (or places). INPUT: - ``S`` -- a list of rational primes, the infinite place as real embedding of `\QQ` or as -1 - ``b`` -- a non-zero rational number which is a non-square locally at every prime in ``S``. - ``check`` -- ``bool`` (default:``True``) perform additional checks on input and confirm the output. OUTPUT: - An integer `a` that has negative Hilbert symbol `(a,b)_p` for every place `p` in `S` and no other place. EXAMPLES:: sage: QQ.hilbert_symbol_negative_at_S([-1,5,3,2,7,11,13,23], -10/7) -9867 sage: QQ.hilbert_symbol_negative_at_S([3, 5, QQ.places()[0], 11], -15) -33 sage: QQ.hilbert_symbol_negative_at_S([3, 5], 2) 15 TESTS:: sage: QQ.hilbert_symbol_negative_at_S(5/2, -2) Traceback (most recent call last): ... TypeError: first argument must be a list or integer :: sage: QQ.hilbert_symbol_negative_at_S([1, 3], 0) Traceback (most recent call last): ... ValueError: second argument must be nonzero :: sage: QQ.hilbert_symbol_negative_at_S([-1, 3, 5], 2) Traceback (most recent call last): ... ValueError: list should be of even cardinality :: sage: QQ.hilbert_symbol_negative_at_S([1, 3], 2) Traceback (most recent call last): ... ValueError: all entries in list must be prime or -1 for infinite place :: sage: QQ.hilbert_symbol_negative_at_S([5, 7], 2) Traceback (most recent call last): ... ValueError: second argument must be a nonsquare with respect to every finite prime in the list :: sage: QQ.hilbert_symbol_negative_at_S([1, 3], sqrt(2)) Traceback (most recent call last): ... TypeError: second argument must be a rational number :: sage: QQ.hilbert_symbol_negative_at_S([-1, 3], 2) Traceback (most recent call last): ... ValueError: if the infinite place is in the list, the second argument must be negative AUTHORS: - Simon Brandhorst, Juanita Duque, Anna Haensch, Manami Roy, Sandi Rudzinski (10-24-2017) """ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.padics.factory import Qp from sage.modules.free_module import VectorSpace from sage.matrix.constructor import matrix from sage.sets.primes import Primes from sage.arith.misc import hilbert_symbol, is_prime # input checks if not type(S) is list: raise TypeError("first argument must be a list or integer") # -1 is used for the infinite place infty = -1 for i in range(len(S)): if S[i] == self.places()[0]: S[i] = -1 if not b in self: raise TypeError("second argument must be a rational number") b = self(b) if b == 0: raise ValueError("second argument must be nonzero") if len(S) % 2 != 0: raise ValueError("list should be of even cardinality") for p in S: if p != infty: if check and not is_prime(p): raise ValueError("all entries in list must be prime" " or -1 for infinite place") R = Qp(p) if R(b).is_square(): raise ValueError( "second argument must be a nonsquare with" " respect to every finite prime in the list") elif b > 0: raise ValueError("if the infinite place is in the list, " "the second argument must be negative") # L is the list of primes that we need to consider, b must have # nonzero valuation for each prime in L, this is the set S' # in Kirschmer's algorithm L = [] L = [p[0] for p in b.factor() if p[0] not in S] # We must also consider 2 to be in L if 2 not in L and 2 not in S: L.append(2) # This adds the infinite place to L if b < 0 and infty not in S: L.append(infty) P = S + L # This constructs the vector v in the algorithm. This is the vector # that we are searching for. It represents the case when the Hilbert # symbol is negative for all primes in S and positive # at all primes in S' V = VectorSpace(GF(2), len(P)) v = V([1] * len(S) + [0] * len(L)) # Compute the map phi of Hilbert symbols at all the primes # in S and S' # For technical reasons, a Hilbert symbol of -1 is # respresented as 1 and a Hilbert symbol of 1 # is represented as 0 def phi(x): v = [(1 - hilbert_symbol(x, b, p)) // 2 for p in P] return V(v) M = matrix(GF(2), [phi(p) for p in P + [-1]]) # We search through all the primes for q in Primes(): # Only look at this prime if it is not in our list if q in P: continue # The algorithm terminates when the vector v is in the # subspace of V generated by the image of the phi map # on the set of generators w = phi(q) W = M.stack(matrix(w)) if v in W.row_space(): break Pq = P + [-1] + [q] l = W.solve_left(v) a = self.prod([Pq[i]**ZZ(l[i]) for i in range(l.degree())]) if check: assert phi(a) == v, "oops" return a
def morphism_matrix(self, f): return matrix(self.domain().base_ring(), [f(b).to_vector() for b in self.domain().basis()]).transpose()
def rho(self, g): r""" Calculate the action of the group element `g` on the type space. EXAMPLES:: sage: from sage.modular.local_comp.type_space import example_type_space sage: T = example_type_space(2) sage: m = T.rho([2,0,0,1]); m [ 1 -2 1 0] [ 1 -1 0 1] [ 1 0 -1 1] [ 0 1 -2 1] sage: v = T.eigensymbol_subspace().basis()[0] sage: m * v == v True We test that it is a left action:: sage: T = example_type_space(0) sage: a = [0,5,4,3]; b = [0,2,3,5]; ab = [1,4,2,2] sage: T.rho(ab) == T.rho(a) * T.rho(b) True An odd level example:: sage: from sage.modular.local_comp.type_space import TypeSpace sage: T = TypeSpace(Newform('54a'), 3) sage: a = [0,1,3,0]; b = [2,1,0,1]; ab = [0,1,6,3] sage: T.rho(ab) == T.rho(a) * T.rho(b) True """ if not self.is_minimal(): raise NotImplementedError( "Group action on non-minimal type space not implemented") if self.u() == 0: # silly special case: rep is principal series or special, so SL2 # action on type space is trivial raise ValueError("Representation is not supercuspidal") p = self.prime() f = p**self.u() g = [ZZ(_) for _ in g] d = (g[0] * g[3] - g[2] * g[1]) # g is in S(K_0) (easy case) if d % f == 1: return self._rho_s(g) # g is in K_0, but not in S(K_0) if d % p != 0: try: a = self._a except AttributeError: self._discover_torus_action() a = self._a i = 0 while (d * a**i) % f != 1: i += 1 if i > f: raise ArithmeticError return self._rho_s([a**i * g[0], g[1], a**i * g[2], g[3] ]) * self._amat**(-i) # funny business if (self.conductor() % 2 == 0): if all([x.valuation(p) > 0 for x in g]): eps = self.form().character()(crt(1, p, f, self.tame_level())) return ~eps * self.rho([x // p for x in g]) else: raise ArithmeticError("g(={0}) not in K".format(g)) else: m = matrix(ZZ, 2, g) s = m.det().valuation(p) mm = (matrix(QQ, 2, [0, -1, p, 0])**(-s) * m).change_ring(ZZ) return self._unif_ramified()**s * self.rho(mm.list())
def _find_cyclic_isomorphism_matching_edge(self, polytope, polytope_origin, p_ray_left, p_ray_right): """ Helper to find an isomorphism of polygons INPUT: - ``polytope`` -- the lattice polytope to compare to. - ``polytope_origin`` -- `\ZZ`-vector. a vertex of ``polytope`` - ``p_ray_left`` - vector. the vector from ``polytope_origin`` to one of its neighboring vertices. - ``p_ray_right`` - vector. the vector from ``polytope_origin`` to the other neighboring vertices. OUTPUT: The element of the lattice Euclidean group that maps ``self`` to ``polytope`` with given origin and left/right neighboring vertex. A :class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError` is raised if no such isomorphism exists. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0)) sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1)) sage: v0, v1, v2 = L2.vertices() sage: L1._find_cyclic_isomorphism_matching_edge(L2, v0, v1-v0, v2-v0) The map A*x+b with A= [ 0 1] [-1 -1] [ 1 3] b = (0, 1, 0) """ from sage.geometry.polyhedron.lattice_euclidean_group_element import \ LatticePolytopesNotIsomorphicError polytope_matrix = block_matrix( 1, 2, [p_ray_left.column(), p_ray_right.column()]) self_vertices = self.ordered_vertices() for i in range(len(self_vertices)): # three consecutive vertices v_left = self_vertices[(i + 0) % len(self_vertices)] v_origin = self_vertices[(i + 1) % len(self_vertices)] v_right = self_vertices[(i + 2) % len(self_vertices)] r_left = v_left - v_origin r_right = v_right - v_origin self_matrix = block_matrix( 1, 2, [r_left.column(), r_right.column()]) A = self_matrix.solve_left(polytope_matrix) b = polytope_origin - A * v_origin try: A = matrix(ZZ, A) b = vector(ZZ, b) except TypeError: continue if A.elementary_divisors()[0:2] != [1, 1]: continue hom = LatticeEuclideanGroupElement(A, b) if hom(self) == polytope: return hom raise LatticePolytopesNotIsomorphicError('different polygons')
def __call__(self, x, check=True): r""" Convert x into an element of this Hecke algebra. Here x is either: - an element of a Hecke algebra equal to this one - an element of the corresponding anemic Hecke algebra, if x is a full Hecke algebra - an element of the corresponding full Hecke algebra of the form `T_i` where i is coprime to ``self.level()``, if self is an anemic Hecke algebra - something that can be converted into an element of the underlying matrix space. In the last case, the parameter ``check'' controls whether or not to check that this element really does lie in the appropriate algebra. At present, setting ``check=True'' raises a NotImplementedError unless x is a scalar (or a diagonal matrix). EXAMPLES:: sage: T = ModularSymbols(11).hecke_algebra() sage: T.gen(2) in T True sage: 5 in T True sage: T.gen(2).matrix() in T Traceback (most recent call last): ... NotImplementedError: Membership testing for '...' not implemented sage: T(T.gen(2).matrix(), check=False) Hecke operator on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field defined by: [ 3 0 -1] [ 0 -2 0] [ 0 0 -2] sage: A = ModularSymbols(11).anemic_hecke_algebra() sage: A(T.gen(3)) Hecke operator T_3 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field sage: A(T.gen(11)) Traceback (most recent call last): ... TypeError: Don't know how to construct an element of Anemic Hecke algebra acting on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field from Hecke operator T_11 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field TESTS: We test that coercion is OK between the Hecke algebras associated to two submodules which are equal but have different bases:: sage: M = CuspForms(Gamma0(57)) sage: f1,f2,f3 = M.newforms() sage: N1 = M.submodule(M.free_module().submodule_with_basis([f1.element().element(), f2.element().element()])) sage: N2 = M.submodule(M.free_module().submodule_with_basis([f1.element().element(), (f1.element() + f2.element()).element()])) sage: N1.hecke_operator(5).matrix_form() Hecke operator on Modular Forms subspace of dimension 2 of ... defined by: [-3 0] [ 0 1] sage: N2.hecke_operator(5).matrix_form() Hecke operator on Modular Forms subspace of dimension 2 of ... defined by: [-3 0] [-4 1] sage: N1.hecke_algebra()(N2.hecke_operator(5)).matrix_form() Hecke operator on Modular Forms subspace of dimension 2 of ... defined by: [-3 0] [ 0 1] sage: N1.hecke_algebra()(N2.hecke_operator(5).matrix_form()) Hecke operator on Modular Forms subspace of dimension 2 of ... defined by: [-3 0] [ 0 1] """ try: if not isinstance(x, Element): x = self.base_ring()(x) if x.parent() is self: return x elif hecke_operator.is_HeckeOperator(x): if x.parent() == self \ or (self.is_anemic() == False and x.parent() == self.anemic_subalgebra()) \ or (self.is_anemic() == True and x.parent().anemic_subalgebra() == self and arith.gcd(x.index(), self.level()) == 1): return hecke_operator.HeckeOperator(self, x.index()) else: raise TypeError elif hecke_operator.is_HeckeAlgebraElement(x): if x.parent() == self or (self.is_anemic() == False and x.parent() == self.anemic_subalgebra()): if x.parent().module().basis_matrix() == self.module( ).basis_matrix(): return hecke_operator.HeckeAlgebraElement_matrix( self, x.matrix()) else: A = matrix([self.module().coordinate_vector(x.parent().module().gen(i)) \ for i in xrange(x.parent().module().rank())]) return hecke_operator.HeckeAlgebraElement_matrix( self, ~A * x.matrix() * A) elif x.parent() == self.anemic_subalgebra(): pass else: raise TypeError else: A = self.matrix_space()(x) if check: if not A.is_scalar(): raise NotImplementedError( "Membership testing for '%s' not implemented" % self) return hecke_operator.HeckeAlgebraElement_matrix(self, A) except TypeError: raise TypeError( "Don't know how to construct an element of %s from %s" % (self, x))
def parity_check_matrix(self): r""" Return the parity check matrix of ``self``. The parity check matrix of a linear code `C` corresponds to the generator matrix of the dual code of `C`. Parity check matrices of all Golay codes are known, and are thus returned by this method without performing any computation. EXAMPLES:: sage: C = codes.GolayCode(GF(3), extended=False) sage: C.parity_check_matrix() [1 0 0 0 0 1 2 2 2 1 0] [0 1 0 0 0 0 1 2 2 2 1] [0 0 1 0 0 2 1 2 0 1 2] [0 0 0 1 0 1 1 0 1 1 1] [0 0 0 0 1 2 2 2 1 0 1] """ n = self.length() if n == 23: H = matrix(GF(2), [[ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0 ], [ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1 ], [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0 ], [ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1 ], [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1 ], [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1 ], [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1 ]]) elif n == 11: H = matrix(GF(3), [[1, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0], [0, 1, 0, 0, 0, 0, 1, 2, 2, 2, 1], [0, 0, 1, 0, 0, 2, 1, 2, 0, 1, 2], [0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1], [0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 1]]) else: H = self.generator_matrix() return H
def period(self, m): """ Return the period of the binary recurrence sequence modulo an integer ``m``. If `n_1` is congruent to `n_2` modulo ``period(m)``, then `u_{n_1}` is is congruent to `u_{n_2}` modulo ``m``. INPUT: - ``m`` -- an integer (modulo which the period of the recurrence relation is calculated). OUTPUT: - The integer (the period of the sequence modulo m) EXAMPLES: If `p = \\pm 1 \\mod 5`, then the period of the Fibonacci sequence mod `p` is `p-1` (c.f. Lemma 3.3 of [BMS2006]_). :: sage: R = BinaryRecurrenceSequence(1,1) sage: R.period(31) 30 sage: [R(i) % 4 for i in range(12)] [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1] sage: R.period(4) 6 This function works for degenerate sequences as well. :: sage: S = BinaryRecurrenceSequence(2,0,1,2) sage: S.is_degenerate() True sage: S.is_geometric() True sage: [S(i) % 17 for i in range(16)] [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9] sage: S.period(17) 8 .. NOTE:: The answer is cached. """ #If we have already computed the period mod m, then we return the stored value. if m in self._period_dict: return self._period_dict[m] else: R = Integers(m) A = matrix(R, [[0, 1], [self.c, self.b]]) w = vector(R, [self.u0, self.u1]) Fac = list(m.factor()) Periods = {} #To compute the period mod m, we compute the least integer n such that A^n*w == w. This necessarily #divides the order of A as a matrix in GL_2(Z/mZ). #We compute the period modulo all distinct prime powers dividing m, and combine via the lcm. #To compute the period mod p^e, we first compute the order mod p. Then the period mod p^e #must divide p^{4e-4}*period(p), as the subgroup of matrices mod p^e, which reduce to #the identity mod p is of order (p^{e-1})^4. So we compute the period mod p^e by successively #multiplying the period mod p by powers of p. for i in Fac: p = i[0] e = i[1] #first compute the period mod p if p in self._period_dict: perp = self._period_dict[p] else: F = A.change_ring(GF(p)) v = w.change_ring(GF(p)) FF = F**(p-1) p1fac = list((p-1).factor()) #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1). #The order divides p-1 if it is diagonalizable. In any case, det(F^(p-1))=1, #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]]. #The order of the subgroup of matrices of this form is p, so the order must divide #p(p-1) -- in fact it must be a multiple of p. If this is not the case, then the #order divides (p-1)(p+1). As the period divides the order of the matrix in GL_2(F_p), #these conditions hold for the period as well. #check if the order divides (p-1) if FF*v == v: M = p-1 Mfac = p1fac #check if the trace is 2, then the order is a multiple of p dividing p*(p-1) elif FF.trace() == 2: M = p-1 Mfac = p1fac F = F**p #replace F by F^p as now we only need to determine the factor dividing (p-1) #otherwise it will divide (p+1)(p-1) else: M = (p+1)*(p-1) p2fac = list((p+1).factor()) #factor the (p+1) and (p-1) terms separately and then combine for speed Mfac_dic = {} for i0, i1 in list(p1fac + p2fac): if i0 not in Mfac_dic: Mfac_dic[i0] = i1 else: Mfac_dic[i0] += i1 Mfac = list(Mfac_dic.items()) #Now use a fast order algorithm to compute the period. We know that the period divides #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors. As #F^M*v == v, for each i_j, if F^(M/i_j)*v == v, then the period divides (M/i_j). After #all factors have been iterated over, the result is the period mod p. Mfac = list(Mfac) C = [] #expand the list of prime factors so every factor is with multiplicity 1 for i0, i1 in Mfac: for j in range(i1): C.append(i0) Mfac = C n = M for ii in Mfac: b = n // ii if F**b * v == v: n = b perp = n #Now compute the period mod p^e by stepping up by multiples of p F = A.change_ring(Integers(p**e)) v = w.change_ring(Integers(p**e)) FF = F**perp if FF*v == v: perpe = perp else: tries = 0 while True: tries += 1 FF = FF**p if FF*v == v: perpe = perp*p**tries break Periods[p] = perpe #take the lcm of the periods mod all distinct primes dividing m period = 1 for p in Periods: period = lcm(Periods[p], period) self._period_dict[m] = period #cache the period mod m return period
def _symmetric_form_matrix(self): r""" Return the matrix for the symmetric form `( | )` in the weight lattice basis. Let `A` be a symmetrizable Cartan matrix with symmetrizer `D`,. This returns the matrix `M^t DA M`, where `M` is dependent upon the type given below. In finite types, `M` is the inverse of the Cartan matrix. In affine types, `M` takes the basis `(\Lambda_0, \Lambda_1, \ldots, \Lambda_r, \delta)` to `(\alpha_0, \ldots, \alpha_r, \Lambda_0)` where `r` is the rank of ``self``. This is used in computing the symmetric form for affine root systems. EXAMPLES:: sage: P = RootSystem(['C',2]).weight_lattice() sage: P._symmetric_form_matrix [1 1] [1 2] sage: P = RootSystem(['C',2,1]).weight_lattice() sage: P._symmetric_form_matrix [0 0 0 1] [0 1 1 1] [0 1 2 1] [1 1 1 0] sage: P = RootSystem(['A',4,2]).weight_lattice() sage: P._symmetric_form_matrix [ 0 0 0 1/2] [ 0 2 2 1] [ 0 2 4 1] [1/2 1 1 0] """ from sage.matrix.constructor import matrix ct = self.cartan_type() cm = ct.cartan_matrix() if cm.det() != 0: cm_inv = cm.inverse() diag = cm.is_symmetrizable(True) return cm_inv.transpose() * matrix.diagonal(diag) if not ct.is_affine(): raise ValueError("only implemented for affine types when the" " Cartan matrix is singular") r = ct.rank() a = ct.a() # Determine the change of basis matrix # La[0], ..., La[r], delta -> al[0], ..., al[r], La[0] M = cm.stack(matrix([1] + [0] * (r - 1))) M = matrix.block([[M, matrix([[1]] + [[0]] * r)]]) M = M.inverse() if a[0] != 1: from sage.rings.all import QQ S = matrix([~a[0]] + [0] * (r - 1)) A = cm.symmetrized_matrix().change_ring(QQ).stack(S) else: A = cm.symmetrized_matrix().stack(matrix([1] + [0] * (r - 1))) A = matrix.block([[A, matrix([[~a[0]]] + [[0]] * r)]]) return M.transpose() * A * M
def matrix(self, basis1=None, basis2=None): r""" Return the matrix of ``self`` w.r.t to a pair of bases. If the matrix is not known already, it is computed from the matrix in another pair of bases by means of the change-of-basis formula. INPUT: - ``basis1`` -- (default: ``None``) basis of the free module on which ``self`` is defined; if none is provided, the module's default basis is assumed - ``basis2`` -- (default: ``None``) basis of the free module on which ``self`` is defined; if none is provided, ``basis2`` is set to ``basis1`` OUTPUT: - the matrix representing representing the automorphism ``self`` w.r.t to bases ``basis1`` and ``basis2``; more precisely, the columns of this matrix are formed by the components w.r.t. ``basis2`` of the images of the elements of ``basis1``. EXAMPLES: Matrices of an automorphism of a rank-3 free `\ZZ`-module:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) sage: e = M.basis('e') sage: a = M.automorphism([[-1,0,0],[0,1,2],[0,1,3]], name='a') sage: a.matrix(e) [-1 0 0] [ 0 1 2] [ 0 1 3] sage: a.matrix() [-1 0 0] [ 0 1 2] [ 0 1 3] sage: f = M.basis('f', from_family=(-e[2], 4*e[1]+3*e[3], 7*e[1]+5*e[3])) ; f Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring sage: a.matrix(f) [ 1 -6 -10] [ -7 83 140] [ 4 -48 -81] Check of the above matrix:: sage: a(f[1]).display(f) a(f_1) = f_1 - 7 f_2 + 4 f_3 sage: a(f[2]).display(f) a(f_2) = -6 f_1 + 83 f_2 - 48 f_3 sage: a(f[3]).display(f) a(f_3) = -10 f_1 + 140 f_2 - 81 f_3 Check of the change-of-basis formula:: sage: P = M.change_of_basis(e,f).matrix(e) sage: a.matrix(f) == P^(-1) * a.matrix(e) * P True Check that the matrix of the product of two automorphisms is the product of their matrices:: sage: b = M.change_of_basis(e,f) ; b Automorphism of the Rank-3 free module M over the Integer Ring sage: b.matrix(e) [ 0 4 7] [-1 0 0] [ 0 3 5] sage: (a*b).matrix(e) == a.matrix(e) * b.matrix(e) True Check that the matrix of the inverse automorphism is the inverse of the automorphism's matrix:: sage: (~a).matrix(e) [-1 0 0] [ 0 3 -2] [ 0 -1 1] sage: (~a).matrix(e) == ~(a.matrix(e)) True Matrices of the identity map:: sage: id = M.identity_map() sage: id.matrix(e) [1 0 0] [0 1 0] [0 0 1] sage: id.matrix(f) [1 0 0] [0 1 0] [0 0 1] """ from sage.matrix.constructor import matrix fmodule = self._fmodule if basis1 is None: basis1 = fmodule.default_basis() elif basis1 not in fmodule.bases(): raise TypeError("{} is not a basis on the {}".format( basis1, fmodule)) if basis2 is None: basis2 = basis1 elif basis2 not in fmodule.bases(): raise TypeError("{} is not a basis on the {}".format( basis2, fmodule)) if (basis1, basis2) not in self._matrices: if basis2 == basis1: comp = self.components(basis1) mat = [[comp[[i, j]] for j in fmodule.irange()] for i in fmodule.irange()] self._matrices[(basis1, basis1)] = matrix(mat) else: # 1/ determine the matrix w.r.t. basis1: self.matrix(basis1) # 2/ perform the change (basis1, basis1) --> (basis1, basis2): raise NotImplementedError( "basis1 != basis2 not implemented yet") return self._matrices[(basis1, basis2)]
def maximal_grading(L): r""" Return a maximal grading of a Lie algebra defined over an algebraically closed field. A maximal grading of a Lie algebra `\mathfrak{g}` is the finest possible grading of `\mathfrak{g}` over torsion free abelian groups. If `\mathfrak{g} = \bigoplus_{n\in \mathbb{Z}^k} \mathfrak{g}_n` is a maximal grading, then there exists no other grading `\mathfrak{g} = \bigoplus_{a\in A} \mathfrak{g}_a` over a torsion free abelian group `A` such that every `\mathfrak{g}_a` is contained in some `\mathfrak{g}_n`. EXAMPLES: A maximal grading of an abelian Lie algebra puts each basis element into an independent layer:: sage: import sys, pathlib sage: sys.path.append(str(pathlib.Path().absolute())) sage: from lie_gradings.gradings.grading import maximal_grading sage: L = LieAlgebra(QQbar, 4, abelian=True) sage: maximal_grading(L) Grading over Additive abelian group isomorphic to Z + Z + Z + Z of Abelian Lie algebra on 4 generators (L[0], L[1], L[2], L[3]) over Algebraic Field with nonzero layers (1, 0, 0, 0) : (L[3],) (0, 1, 0, 0) : (L[2],) (0, 0, 1, 0) : (L[1],) (0, 0, 0, 1) : (L[0],) A maximal grading of a free nilpotent Lie algebra decomposes the Lie algebra based on how many times each generator appears in the defining Lie bracket:: sage: L = LieAlgebra(QQbar, 3, step=3) sage: maximal_grading(L) Grading over Additive abelian group isomorphic to Z + Z + Z of Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Algebraic Field with nonzero layers (1, 0, 0) : (X_1,) (0, 1, 0) : (X_2,) (1, 1, 0) : (X_12,) (1, 2, 0) : (X_122,) (2, 1, 0) : (X_112,) (0, 0, 1) : (X_3,) (0, 1, 1) : (X_23,) (0, 1, 2) : (X_233,) (0, 2, 1) : (X_223,) (1, 0, 1) : (X_13,) (1, 0, 2) : (X_133,) (1, 1, 1) : (X_123, X_132) (2, 0, 1) : (X_113,) """ # define utilities to convert from matrices to vectors and back R = L.base_ring() n = L.dimension() MS = MatrixSpace(R, n, n) def matrix_to_vec(A): return vector(R, sum((list(Ar) for Ar in A.rows()), [])) db = L.derivations_basis() def derivation_lincomb(vec): return sum((vk * dk for vk, dk in zip(vec, db)), MS.zero()) # iteratively construct larger and larger tori of derivations t = [] while True: # compute the centralizer of the torus in the derivation algebra ker = FreeModule(R, len(db)) for der in t: # form the matrix of ad(der) with rows # the images of basis derivations A = matrix([matrix_to_vec(der * X - X * der) for X in db]) ker = ker.intersection(A.left_kernel()) cb = [derivation_lincomb(v) for v in ker.basis()] # check the basis of the centralizer for semisimple parts outside of t gl = FreeModule(R, n * n) t_submodule = gl.submodule([matrix_to_vec(der) for der in t]) for A in cb: As, An = jordan_decomposition(A) if matrix_to_vec(As) not in t_submodule: # extend the torus by As t.append(As) break else: # no new elements found, so the torus is maximal break # compute the eigenspace intersections to get the concrete grading common_eigenspaces = [([], FreeModule(R, n))] for A in t: new_eigenspaces = [] eig = A.right_eigenspaces() for ev, V in common_eigenspaces: for ew, W in eig: VW = V.intersection(W) if VW.dimension() > 0: new_eigenspaces.append((ev + [ew], VW)) common_eigenspaces = new_eigenspaces if not t: # zero dimensional maximal torus # the only grading is the trivial grading magma = AdditiveAbelianGroup([]) layers = {magma.zero(): L.basis().list()} return grading(L, layers, magma=magma) # define a grading with layers indexed by tuples of eigenvalues cm = get_coercion_model() all_eigenvalues = sum((ev for ev, V in common_eigenspaces), []) k = len(common_eigenspaces[0][0]) evR = cm.common_parent(*all_eigenvalues) layers = { tuple(ev): [L.from_vector(v) for v in V.basis()] for ev, V in common_eigenspaces } magma = evR.cartesian_product(*[evR] * (k - 1)) gr = grading(L, layers, magma=magma) # convert to a grading over Z^k return gr.universal_realization()
def stratification(L): r""" Return a stratification of the Lie algebra if one exists. INPUT: - ``L`` -- a Lie algebra OUTPUT: A grading of the Lie algebra `\mathfrak{g}` over the integers such that the layer `\mathfrak{g}_1` generates the full Lie algebra. EXAMPLES:: A stratification for a free nilpotent Lie algebra is the one based on the length of the defining bracket:: sage: from lie_gradings.gradings.grading import stratification sage: from lie_gradings.gradings.utilities import in_new_basis sage: L = LieAlgebra(QQ, 3, step=3) sage: strat = stratification(L) sage: strat Grading over Additive abelian group isomorphic to Z of Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Rational Field with nonzero layers (1) : (X_1, X_2, X_3) (2) : (X_12, X_13, X_23) (3) : (X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) The main use case is when the original basis of the stratifiable Lie algebra is not adapted to a stratification. Consider the following quotient Lie algebra:: sage: X_1, X_2, X_3 = L.basis().list()[:3] sage: Q = L.quotient(L[X_2, X_3]) sage: Q Lie algebra quotient L/I of dimension 10 over Rational Field where L: Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Rational Field I: Ideal (X_23) We switch to a basis which does not define a stratification:: sage: Y_1 = Q(X_1) sage: Y_2 = Q(X_2) + Q[X_1, X_2] sage: Y_3 = Q(X_3) sage: basis = [Y_1, Y_2, Y_3] + Q.basis().list()[3:] sage: Y_labels = ["Y_%d"%(k+1) for k in range(len(basis))] sage: K = in_new_basis(Q, basis,Y_labels) sage: K.inject_variables() Defining Y_1, Y_2, Y_3, Y_4, Y_5, Y_6, Y_7, Y_8, Y_9, Y_10 sage: K[Y_2, Y_3] Y_9 sage: K[[Y_1, Y_3], Y_2] Y_9 We may reconstruct a stratification in the new basis without any knowledge of the original stratification:: sage: stratification(K) Grading over Additive abelian group isomorphic to Z of Nilpotent Lie algebra on 10 generators (Y_1, Y_2, Y_3, Y_4, Y_5, Y_6, Y_7, Y_8, Y_9, Y_10) over Rational Field with nonzero layers (1) : (Y_1, Y_2 - Y_4, Y_3) (2) : (Y_4, Y_5) (3) : (Y_10, Y_6, Y_7, Y_8, Y_9) sage: K[Y_1, Y_2 - Y_4] Y_4 sage: K[Y_1, Y_3] Y_5 sage: K[Y_2 - Y_4, Y_3] 0 A non-stratifiable Lie algebra raises an error:: sage: L = LieAlgebra(QQ, {('X_1','X_3'): {'X_4': 1}, ....: ('X_1','X_4'): {'X_5': 1}, ....: ('X_2','X_3'): {'X_5': 1}}, ....: names='X_1,X_2,X_3,X_4,X_5') sage: stratification(L) Traceback (most recent call last): ... ValueError: Lie algebra on 5 generators (X_1, X_2, X_3, X_4, X_5) over Rational Field is not a stratifiable Lie algebra """ lcs = L.lower_central_series(submodule=True) quots = [V.quotient(W) for V, W in zip(lcs, lcs[1:])] # find a basis adapted to the filtration by the lower central series adapted_basis = [] weights = [] for k, q in enumerate(quots): weights += [k + 1] * q.dimension() for v in q.basis(): b = q.lift(v) adapted_basis.append(b) # define a submodule to compute structural # coefficients in the filtration adapted basis try: m = L.module() except AttributeError: m = FreeModule(L.base_ring(), L.dimension()) sm = m.submodule_with_basis(adapted_basis) # form the linear system Ax=b of constraints from the Leibniz rule paramspace = [(k, h) for k in range(L.dimension()) for h in range(L.dimension()) if weights[h] > weights[k]] Arows = [] bvec = [] zerovec = m.zero() for i in range(L.dimension()): Y_i = adapted_basis[i] w_i = weights[i] for j in range(i + 1, L.dimension()): Y_j = adapted_basis[j] w_j = weights[j] Y_ij = L.bracket(Y_i, Y_j) c_ij = sm.coordinate_vector(Y_ij.to_vector()) bcomp = L.zero() for k in range(L.dimension()): w_k = weights[k] Y_k = adapted_basis[k] bcomp += (w_k - w_i - w_j) * c_ij[k] * Y_k bv = bcomp.to_vector() Acomp = {} for k, h in paramspace: w_k = weights[k] Y_h = adapted_basis[h] if k == i: Acomp[(k, h)] = L.bracket(Y_h, Y_j).to_vector() elif k == j: Acomp[(k, h)] = L.bracket(Y_i, Y_h).to_vector() elif w_k >= w_i + w_j: Acomp[(k, h)] = -c_ij[k] * Y_h for r in range(L.dimension()): Arows.append( [Acomp.get((k, h), zerovec)[r] for k, h in paramspace]) bvec.append(bv[r]) A = matrix(L.base_ring(), Arows) b = vector(L.base_ring(), bvec) # solve the linear system Ax=b if possible try: coeffs_flat = A.solve_right(b) except ValueError: raise ValueError("%s is not a stratifiable Lie algebra" % L) coeffs = {(k, h): ckh for (k, h), ckh in zip(paramspace, coeffs_flat)} # define the matrix of the derivation determined by the solution # in the adapted basis cols = [] for k in range(L.dimension()): w_k = weights[k] Y_k = adapted_basis[k] hspace = [h for (l, h) in paramspace if l == k] Yk_im = w_k * Y_k + sum( (coeffs[(k, h)] * adapted_basis[h] for h in hspace), L.zero()) cols.append(sm.coordinate_vector(Yk_im.to_vector())) der = matrix(L.base_ring(), cols).transpose() # the layers of the stratification are V_k = ker(der - kI) layers = {} for k in range(len(quots)): degree = k + 1 B = der - degree * matrix.identity(L.dimension()) adapted_kernel = B.right_kernel() # convert back to the original basis Vk_basis = [sm.from_vector(X) for X in adapted_kernel.basis()] layers[(degree, )] = [ L.from_vector(v) for v in m.submodule(Vk_basis).basis() ] return grading(L, layers, magma=AdditiveAbelianGroup([0]), projections=True)