def __init__(self, projection_direction, height = 1.1): """ Initializes the projection. EXAMPLES:: sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel sage: proj = ProjectionFuncSchlegel([2,2,2]) sage: proj.__init__([2,2,2]) sage: proj(vector([1.1,1.1,1.11]))[0] 0.0302... sage: TestSuite(proj).run(skip='_test_pickling') """ self.projection_dir = vector(RDF, projection_direction) if norm(self.projection_dir).is_zero(): raise ValueError, "projection direction must be a non-zero vector." self.dim = self.projection_dir.degree() spcenter = height * self.projection_dir/norm(self.projection_dir) self.height = height v = vector(RDF, [0.0]*(self.dim-1) + [self.height]) - spcenter polediff = matrix(RDF,v).transpose() denom = (polediff.transpose()*polediff)[0][0] if denom.is_zero(): self.house = identity_matrix(RDF,self.dim) else: self.house = identity_matrix(RDF,self.dim) \ - 2*polediff*polediff.transpose()/denom #Householder reflector
def mutate(self, k, mutating_F=True): r""" mutate seed bla bla ba """ n = self.parent().rk if k not in xrange(n): raise ValueError('Cannot mutate in direction ' + str(k) + '.') # store mutation path if self._path != [] and self._path[-1] == k: self._path.pop() else: self._path.append(k) # find sign of k-th c-vector # Will this be used enough that it should be a built-in function? if any(x > 0 for x in self._C.column(k)): eps = +1 else: eps = -1 # store the g-vector to be mutated in case we are mutating F-polynomials also old_g_vector = self.g_vector(k) # G-matrix J = identity_matrix(n) for j in xrange(n): J[j,k] += max(0, -eps*self._B[j,k]) J[k,k] = -1 self._G = self._G*J # g-vector path list g_vector = self.g_vector(k) if g_vector not in self.parent().g_vectors_so_far(): self.parent()._path_dict[g_vector] = copy(self._path) # F-polynomials if mutating_F: self.parent()._F_poly_dict[g_vector] = self._mutated_F(k, old_g_vector) # C-matrix J = identity_matrix(n) for j in xrange(n): J[k,j] += max(0, eps*self._B[k,j]) J[k,k] = -1 self._C = self._C*J # B-matrix self._B.mutate(k) # exchange relation if self.parent()._store_exchange_relations: ex_pair = frozenset([g_vector,old_g_vector]) if ex_pair not in self.parent()._exchange_relations: coefficient = self.coefficient(k).lift() variables = zip(self.g_vectors(), self.b_matrix().column(k)) Mp = [ (g,p) for (g,p) in variables if p > 0 ] Mm = [ (g,-p) for (g,p) in variables if p < 0 ] self.parent()._exchange_relations[ex_pair] = ( (Mp,coefficient.numerator()), (Mm,coefficient.denominator()) )
def __init__(self, projection_point): """ Create a stereographic projection function. INPUT: - ``projection_point`` -- a list of coordinates in the appropriate dimension, which is the point projected from. EXAMPLES:: sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic sage: proj = ProjectionFuncStereographic([1.0,1.0]) sage: proj.__init__([1.0,1.0]) sage: proj.house [-0.7071067811... 0.7071067811...] [ 0.7071067811... 0.7071067811...] sage: TestSuite(proj).run(skip='_test_pickling') """ self.projection_point = vector(projection_point) self.dim = self.projection_point.degree() pproj = vector(RDF,self.projection_point) self.psize = norm(pproj) if (self.psize).is_zero(): raise ValueError, "projection direction must be a non-zero vector." v = vector(RDF, [0.0]*(self.dim-1) + [self.psize]) - pproj polediff = matrix(RDF,v).transpose() denom = RDF((polediff.transpose()*polediff)[0][0]) if denom.is_zero(): self.house = identity_matrix(RDF,self.dim) else: self.house = identity_matrix(RDF,self.dim) \ - 2*polediff*polediff.transpose()/denom # Householder reflector
def mutate(self, sequence, inplace=True, mutate_F=True): if not isinstance(mutate_F, bool): raise ValueError('mutate_F must be boolean.') if not isinstance(inplace, bool): raise ValueError('inplace must be boolean.') if inplace: seed = self.current_seed else: seed = copy(self.current_seed) if sequence in xrange(seed._n): seq = iter([sequence]) else: seq = iter(sequence) for k in seq: if k not in xrange(seed._n): raise ValueError('Cannot mutate in direction' + str(k) + '.') # G-matrix J = identity_matrix(seed._n) if any(x > 0 for x in seed._C.column(k)): eps = +1 else: eps = -1 for j in xrange(seed._n): J[j,k] += max(0, -eps*seed._B[j,k]) J[k,k] = -1 seed._G = seed._G*J # F-polynomials if mutating_F: gvect = tuple(seed._G.column(k)) if not self._F_dict.has_key(gvect): self._F_dict.setdefault(gvect,self._mutated_F(k)) seed._F[k] = self._F_dict[gvect] # C-matrix J = identity_matrix(seed._n) if any(x > 0 for x in seed._C.column(k)): eps = +1 else: eps = -1 for j in xrange(seed._n): J[k,j] += max(0, eps*seed._B[k,j]) J[k,k] = -1 seed._C = seed._C*J # B-matrix seed._B.mutate(k) if not inplace: return seed
def _extend1(self): "Increases the matrix size by 1" #save current self self.iv0 = copy(self) self.iv0.abel_raw0 = lambda z: self.iv0.abel0poly(z-self.iv0.x0) N = self.N A = self.A #Carleman matrix without 0-th row: Ct = A + identity_matrix(self.R,N).submatrix(1,0,N-1,N-1) AI = self.AI #assert AI*self.A == identity_matrix(N-1) if isinstance(self.f,FormalPowerSeries): coeffs = [ self.f[n] for n in xrange(0,N) ] else: x = self.f.args()[0] coeffs = taylor(self.f.substitute({x:x+self.x0sym})-self.x0sym,x,0,N).polynomial(self.R) self.Ct = matrix(self.R,N,N) self.Ct.set_block(0,0,Ct) self.Ct[0,N-1] = coeffs[N-1] for m in range(1,N-1): self.Ct[m,N-1] = psmul_at(self.Ct[0],self.Ct[m-1],N-1) self.Ct[N-1] = psmul(self.Ct[0],self.Ct[N-2]) print "C extended" self.A = self.Ct - identity_matrix(self.R,N+1).submatrix(1,0,N,N) av=self.A.column(N-1)[:N-1] ah=self.A.row(N-1)[:N-1] a_n=self.A[N-1,N-1] # print 'A:';print A # print 'A\''; print self.A # print 'ah:',ah # print 'av:',av # print 'a_n:',a_n AI0 = matrix(self.R,N,N) AI0.set_block(0,0,self.AI) horiz = matrix(self.R,1,N) horiz.set_block(0,0,(ah*AI).transpose().transpose()) horiz[0,N-1] = -1 vert = matrix(self.R,N,1) vert.set_block(0,0,(AI*av).transpose()) vert[N-1,0] = -1 self.N += 1 self.AI = AI0 + vert*horiz/(a_n-ah*AI*av)
def burau_matrix(self, var='t'): """ Return the Burau matrix of the braid. INPUT: - ``var`` -- string (default: ``'t'``). The name of the variable in the entries of the matrix. OUTPUT: The Burau matrix of the braid. It is a matrix whose entries are Laurent polynomials in the variable ``var``. EXAMPLES:: sage: B = BraidGroup(4) sage: B.inject_variables() Defining s0, s1, s2 sage: b=s0*s1/s2/s1 sage: b.burau_matrix() [ -t + 1 0 -t^2 + t t^2] [ 1 0 0 0] [ 0 0 1 0] [ 0 t^-2 t^-1 - t^-2 1 - t^-1] sage: s2.burau_matrix('x') [ 1 0 0 0] [ 0 1 0 0] [ 0 0 -x + 1 x] [ 0 0 1 0] REFERENCES: http://en.wikipedia.org/wiki/Burau_representation """ R = LaurentPolynomialRing(IntegerRing(), var) t = R.gen() M = identity_matrix(R, self.strands()) for i in self.Tietze(): A = identity_matrix(R, self.strands()) if i>0: A[i-1, i-1] = 1-t A[i, i] = 0 A[i, i-1] = 1 A[i-1, i] = t if i<0: A[-1-i, -1-i] = 0 A[-i, -i] = 1-t**(-1) A[-1-i, -i] = 1 A[-i, -1-i] = t**(-1) M=M*A return M
def mutate_initial(self, k): r""" Mutate ``self`` in direction `k` at the initial cluster. INPUT: - ``k`` -- integer in between 0 and ``self.rk`` """ n = self.rk if k not in xrange(n): raise ValueError('Cannot mutate in direction %s, please try a value between 0 and %s.'%(str(k),str(n-1))) #modify self._path_dict using Nakanishi-Zelevinsky (4.1) and self._F_poly_dict using CA-IV (6.21) new_path_dict = dict() new_F_dict = dict() new_path_dict[tuple(identity_matrix(n).column(k))] = [] new_F_dict[tuple(identity_matrix(n).column(k))] = self._U(1) poly_ring = PolynomialRing(ZZ,'u') h_subs_tuple = tuple([poly_ring.gen(0)**(-1) if j==k else poly_ring.gen(0)**max(-self._B0[k][j],0) for j in xrange(n)]) F_subs_tuple = tuple([self._U.gen(k)**(-1) if j==k else self._U.gen(j)*self._U.gen(k)**max(-self._B0[k][j],0)*(1+self._U.gen(k))**(self._B0[k][j]) for j in xrange(n)]) for g_vect in self._path_dict: #compute new path path = self._path_dict[g_vect] if g_vect == tuple(identity_matrix(n).column(k)): new_path = [k] elif path != []: if path[0] != k: new_path = [k] + path else: new_path = path[1:] else: new_path = [] #compute new g-vector new_g_vect = vector(g_vect) - 2*g_vect[k]*identity_matrix(n).column(k) for i in xrange(n): new_g_vect += max(sign(g_vect[k])*self._B0[i,k],0)*g_vect[k]*identity_matrix(n).column(i) new_path_dict[tuple(new_g_vect)] = new_path #compute new F-polynomial h = 0 trop = tropical_evaluation(self._F_poly_dict[g_vect](h_subs_tuple)) if trop != 1: h = trop.denominator().exponents()[0]-trop.numerator().exponents()[0] new_F_dict[tuple(new_g_vect)] = self._F_poly_dict[g_vect](F_subs_tuple)*self._U.gen(k)**h*(self._U.gen(k)+1)**g_vect[k] self._path_dict = new_path_dict self._F_poly_dict = new_F_dict self._B0.mutate(k)
def initial_seed(self): r""" Return the initial seed """ n = self.rk I = identity_matrix(n) return ClusterAlgebraSeed(self._B0, I, I, self)
def invariant_form(self): """ Return the hermitian form preserved by the unitary group. OUTPUT: A square matrix describing the bilinear form EXAMPLES:: sage: SU4 = SU(4,QQ) sage: SU4.invariant_form() [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] """ if self._invariant_form is not None: return self._invariant_form from sage.matrix.constructor import identity_matrix m = identity_matrix(self.base_ring(), self.degree()) m.set_immutable() return m
def initial_pair(self): """ Return an initial double description pair. Picks an initial set of rays by selecting a basis. This is probably the most efficient way to select the initial set. INPUT: - ``pair_class`` -- subclass of :class:`DoubleDescriptionPair`. OUTPUT: A pair consisting of a :class:`DoubleDescriptionPair` instance and the tuple of remaining unused inequalities. EXAMPLES:: sage: A = matrix([(-1, 1), (-1, 2), (1/2, -1/2), (1/2, 2)]) sage: from sage.geometry.polyhedron.double_description import Problem sage: DD, remaining = Problem(A).initial_pair() sage: DD.verify() sage: remaining [(1/2, -1/2), (1/2, 2)] """ pivot_rows = self.A_matrix().pivot_rows() A0 = [self.A()[pivot] for pivot in pivot_rows] Ac = [self.A()[i] for i in range(len(self.A())) if i not in pivot_rows] from sage.matrix.constructor import identity_matrix, matrix I = identity_matrix(self.base_ring(), self.dim()) R = matrix(self.base_ring(), A0).solve_right(I) return self.pair_class(self, A0, R.columns()), list(Ac)
def ARP(self): from sage.matrix.constructor import identity_matrix 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]) P12 = matrix(3, [1,0,1, 1,1,1, 0,0,1]) P13 = matrix(3, [1,1,0, 0,1,0, 1,1,1]) P23 = matrix(3, [1,0,0, 1,1,0, 1,1,1]) P21 = matrix(3, [1,1,1, 0,1,1, 0,0,1]) P31 = matrix(3, [1,1,1, 0,1,0, 0,1,1]) P32 = matrix(3, [1,0,0, 1,1,1, 1,0,1]) gens = (A1, A2, A3, P23, P32, P13, P31, P12, P21) alphabet = [1, 2, 3, 123, 132, 213, 231, 312, 321] gens = dict(zip(alphabet, gens)) cone = {} cone[123] = H23 = matrix(3, [1,0,1, 0,1,0, 0,0,1]) cone[132] = H32 = matrix(3, [1,1,0, 0,1,0, 0,0,1]) cone[213] = H13 = matrix(3, [1,0,0, 0,1,1, 0,0,1]) cone[231] = H31 = matrix(3, [1,0,0, 1,1,0, 0,0,1]) cone[312] = H12 = matrix(3, [1,0,0, 0,1,0, 0,1,1]) cone[321] = H21 = matrix(3, [1,0,0, 0,1,0, 1,0,1]) cone[1] = cone[2] = cone[3] = identity_matrix(3) from .language import languages return MatrixCocycle(gens, cone, language=languages.ARP())
def calculate_generators(self): """ If generators haven't already been computed, calculate generators for this homspace. If they have been computed, do nothing. EXAMPLES:: sage: E = End(J0(11)) sage: E.calculate_generators() """ if self._gens is not None: return if (self.domain() == self.codomain()) and (self.domain().dimension() == 1): self._gens = tuple([ identity_matrix(ZZ,2) ]) return phi = self.domain()._isogeny_to_product_of_powers() psi = self.codomain()._isogeny_to_product_of_powers() H_simple = phi.codomain().Hom(psi.codomain()) im_gens = H_simple._calculate_product_gens() M = phi.matrix() Mt = psi.complementary_isogeny().matrix() R = ZZ**(4*self.domain().dimension()*self.codomain().dimension()) gens = R.submodule([ (M*self._get_matrix(g)*Mt).list() for g in im_gens ]).saturation().basis() self._gens = tuple([ self._get_matrix(g) for g in gens ])
def calculate_generators(self): """ If generators haven't already been computed, calculate generators for this homspace. If they have been computed, do nothing. EXAMPLES:: sage: E = End(J0(11)) sage: E.calculate_generators() """ if self._gens is not None: return if (self.domain() == self.codomain()) and (self.domain().dimension() == 1): self._gens = tuple([identity_matrix(ZZ, 2)]) return phi = self.domain()._isogeny_to_product_of_powers() psi = self.codomain()._isogeny_to_product_of_powers() H_simple = phi.codomain().Hom(psi.codomain()) im_gens = H_simple._calculate_product_gens() M = phi.matrix() Mt = psi.complementary_isogeny().matrix() R = ZZ**(4 * self.domain().dimension() * self.codomain().dimension()) gens = R.submodule([(M * self._get_matrix(g) * Mt).list() for g in im_gens]).saturation().basis() self._gens = tuple([self._get_matrix(g) for g in gens])
def __init__(self, similarity_surface): if similarity_surface.underlying_surface().is_mutable(): if similarity_surface.is_finite(): self._ss=similarity_surface.copy() else: raise ValueError("Can not construct MinimalTranslationCover of a surface that is mutable and infinite.") else: self._ss = similarity_surface # We are finite if and only if self._ss is a finite RationalConeSurface. if not self._ss.is_finite(): finite = False else: try: from flatsurf.geometry.rational_cone_surface import RationalConeSurface ss_copy = self._ss.reposition_polygons(relabel=True) rcs = RationalConeSurface(ss_copy) rcs._test_edge_matrix() finite=True except AssertionError: # print("Warning: Could be indicating infinite surface falsely.") finite=False I = identity_matrix(self._ss.base_ring(),2) I.set_immutable() base_label=(self._ss.base_label(), I) Surface.__init__(self, self._ss.base_ring(), base_label, finite=finite, mutable=False)
def calculate_voronoi_cell(basis, radius=None, verbose=False): """ Calculate the Voronoi cell of the lattice defined by basis INPUT: - ``basis`` -- embedded basis matrix of the lattice - ``radius`` -- radius of basis vectors to consider - ``verbose`` -- whether to print debug information OUTPUT: A :class:`Polyhedron` instance. EXAMPLES:: sage: from sage.modules.diamond_cutting import calculate_voronoi_cell sage: V = calculate_voronoi_cell(matrix([[1, 0], [0, 1]])) sage: V.volume() 1 """ dim = basis.dimensions() artificial_length = None if dim[0] < dim[1]: # introduce "artificial" basis points (representing infinity) artificial_length = max(abs(v) for v in basis).ceil() * 2 additional_vectors = identity_matrix(dim[1]) * artificial_length basis = basis.stack(additional_vectors) # LLL-reduce to get quadratic matrix basis = basis.LLL() basis = matrix([v for v in basis if v]) dim = basis.dimensions() if dim[0] != dim[1]: raise ValueError("invalid matrix") basis = basis / 2 ieqs = [] for v in basis: ieqs.append(plane_inequality(v)) ieqs.append(plane_inequality(-v)) Q = Polyhedron(ieqs=ieqs) # twice the length of longest vertex in Q is a safe choice if radius is None: radius = 2 * max(abs(v) ** 2 for v in basis) V = diamond_cut(Q, basis, radius, verbose=verbose) if artificial_length is not None: # remove inequalities introduced by artificial basis points H = V.Hrepresentation() H = [v for v in H if all(not V._is_zero(v.A() * w / 2 - v.b() and not V._is_zero(v.A() * (-w) / 2 - v.b())) for w in additional_vectors)] V = Polyhedron(ieqs=H) return V
def step_transition_matrix(step, eps, rows=None, ctx=None, split=0, max_split=3): dop = step.start.dop order = dop.order() if rows is None: rows = order z0, z1 = step if order == 0: logger.info("%s: trivial case", step) return matrix(ZZ) # 0 by 0 elif z0.value == z1.value: logger.info("%s: trivial case", step) return identity_matrix(ZZ, order)[:rows] elif z0.is_ordinary() and z1.is_ordinary(): logger.info("%s: ordinary case", step) logger.debug("fraction of cvrad: %s/%s", step.length(), z0.dist_to_sing()) fun = ordinary_step_transition_matrix elif z0.is_regular() and z1.is_ordinary(): logger.info("%s: regular singular case (going out)", step) logger.debug("fraction of cvrad: %s/%s", step.length(), z0.dist_to_sing()) fun = singular_step_transition_matrix elif z0.is_ordinary() and z1.is_regular(): logger.info("%s: regular singular case (going in)", step) logger.debug("fraction of cvrad: %s/%s", step.length(), z1.dist_to_sing()) fun = inverse_singular_step_transition_matrix else: raise TypeError(type(z0), type(z1)) try: return fun(dop, step, eps, rows, ctx=ctx, fail_fast=(split < max_split)) except (accuracy.PrecisionError, bounds.BoundPrecisionError): # XXX it would be nicer to return something in this case... if split >= max_split: raise logger.info("splitting step...") s0, s1 = step.split() m0 = step_transition_matrix(s0, eps / 2, rows=None, ctx=ctx, split=split + 1) m1 = step_transition_matrix(s1, eps / 2, rows=rows, ctx=ctx, split=split + 1) return m1 * m0
def matrix_power(self,t): eigenvalues = self.eigenvalues iprec = self.iprec CM = self.CM ev = [ e.n(iprec) for e in eigenvalues] n = len(ev) Char = [CM - ev[k] * identity_matrix(n) for k in range(n)] #product till k-1 prodwo = n * [0] prod = identity_matrix(n) #if we were to start here with prod = IdentityMatrix #prodwo[k]/sprodwo[k] would be the component projector of ev[k] #component projector of ev[k] is a matrix Z such that #CM * Z = ev[k] * Z and Z*Z=Z #then f(CM)=sum_k f(ev[k])*Z[k] #as we are only interested in the first line we can start #left with (0,1,...) instead of the identity matrix for k in range(n): prodwo[k] = prod for i in range(k+1,n): prodwo[k] = prodwo[k] * Char[i] if k == n: break prod = prod * Char[k] sprodwo = n * [0] for k in range(n): if k == 0: sprodwo[k] = ev[k] - ev[1] start = 2 else: sprodwo[k] = ev[k] - ev[0] start = 1 for i in range(start,n): if not i == k: sprodwo[k] = sprodwo[k] * (ev[k]-ev[i]) return sum([ev[k]**t/sprodwo[k]*prodwo[k] for k in range(n)])
def reduce(self, t) : ## We compute the rational diagonal form of t. Whenever a zero entry occures we ## find a primitive isotropic vector and apply a base change, such that t finally ## has the form diag(0,0,...,P) where P is positive definite. P will then be a ## LLL reduced. ot = t t = matrix(QQ, t) n = t.nrows() u = identity_matrix(QQ, n) for i in xrange(n) : if t[i,i] < 0 : return None elif t[i,i] == 0 : ## get the isotropic vector v = u.column(i) v = v.denominator() * v v = v / gcd(list(v)) u = self._gln_lift(v, 0) t = u.transpose() * ot * u ts = self.reduce(t.submatrix(1,1,n-1,n-1)) if ts is None : return None t.set_block(1, 1, ts[0]) t.set_immutable() return (t,1) else : for j in xrange(i + 1, n) : us = identity_matrix(QQ, n, n) us[i,j] = -t[i,j]/t[i,i] u = u * us t.add_multiple_of_row(j, i, -t[j,i]/t[i,i]) t.add_multiple_of_column(j, i, -t[i,j]/t[i,i]) u = ot.LLL_gram() t = u.transpose() * ot * u t.set_immutable() return (t, 1)
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 step_transition_matrix(dop, step, eps, rows=None, split=0, ctx=dctx): r""" TESTS:: sage: from ore_algebra.examples import fcc sage: fcc.dop4.numerical_solution([0, 0, 0, 1], [0, 1], 1e-3) [1...] + [+/- ...]*I """ order = dop.order() if rows is None: rows = order z0, z1 = step if order == 0: logger.debug("%s: trivial case", step) return matrix(ZZ) # 0 by 0 elif z0.value == z1.value: logger.debug("%s: trivial case", step) return identity_matrix(ZZ, order)[:rows] elif z0.is_ordinary() and z1.is_ordinary(): logger.info("%s: ordinary case", step) if z0.is_exact(): inverse = False # XXX maybe also invert the step when z1 is much simpler than z0 else: # can happen with the very first step step = Step(z1, z0, max_split=0) inverse = True elif z0.is_regular() and z1.is_ordinary(): logger.info("%s: regular singular case (going out)", step) inverse = False elif z0.is_ordinary() and z1.is_regular(): logger.info("%s: regular singular case (going in)", step) step = Step(z1, z0) inverse = True eps /= 2 else: raise ValueError(z0, z1) try: mat = regular_step_transition_matrix(dop, step, eps, rows, fail_fast=(step.max_split > 0), effort=split, ctx=ctx) except (accuracy.PrecisionError, bounds.BoundPrecisionError): if step.max_split == 0: raise # XXX: can we return something? logger.info("splitting step...") s0, s1 = step.split() m0 = step_transition_matrix(dop, s0, eps / 4, None, split + 1, ctx) m1 = step_transition_matrix(dop, s1, eps / 4, rows, split + 1, ctx) mat = m1 * m0 if inverse: mat = ~mat return mat
def fundamental_matrix_ordinary(dop, pt, eps, rows, maj, max_prec=100000): eps_col = bounds.IR(eps)/bounds.IR(dop.order()).sqrt() evpt = EvaluationPoint(pt, jet_order=rows) inis = [ LogSeriesInitialValues(ZZ.zero(), ini, dop, check=False) for ini in identity_matrix(dop.order())] cols = [ series_sum(dop, ini, evpt, eps_col, maj=maj, max_prec=max_prec) for ini in inis] return matrix(cols).transpose()
def _normalisation_factor_zz(self, tau=3): r""" This function returns an approximation of `∑_{x ∈ \ZZ^n} \exp(-|x|_2^2/(2σ²))`, i.e. the normalisation factor such that the sum over all probabilities is 1 for `\ZZⁿ`. If this ``self.B`` is not an identity matrix over `\ZZ` a ``NotImplementedError`` is raised. INPUT: - ``tau`` -- all vectors `v` with `|v|_∞ ≤ τ·σ` are enumerated (default: ``3``). EXAMPLES:: sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: n = 3; sigma = 1.0 sage: D = DiscreteGaussianDistributionLatticeSampler(ZZ^n, sigma) sage: f = D.f sage: c = D._normalisation_factor_zz(); c 15.528... sage: from collections import defaultdict sage: counter = defaultdict(Integer) sage: m = 0 sage: def add_samples(i): ....: global counter, m ....: for _ in range(i): ....: counter[D()] += 1 ....: m += 1 sage: v = vector(ZZ, n, (0, 0, 0)) sage: v.set_immutable() sage: while v not in counter: add_samples(1000) sage: while abs(m*f(v)*1.0/c/counter[v] - 1.0) >= 0.1: add_samples(1000) sage: v = vector(ZZ, n, (-1, 2, 3)) sage: v.set_immutable() sage: while v not in counter: add_samples(1000) sage: while abs(m*f(v)*1.0/c/counter[v] - 1.0) >= 0.2: add_samples(1000) # long time """ if self.B != identity_matrix(ZZ, self.B.nrows()): raise NotImplementedError( "This function is only implemented when B is an identity matrix." ) f = self.f n = self.B.ncols() sigma = self._sigma return sum( f(x) for x in _iter_vectors(n, -ceil(tau * sigma), ceil(tau * sigma)))
def homogeneous_components(self): deg_matrix = block_matrix([[identity_matrix(self.parent().rk),-self.parent()._B0]]) components = dict() x = self.lift() monomials = x.monomials() for m in monomials: g_vect = tuple(deg_matrix*vector(m.exponents()[0])) if g_vect in components: components[g_vect] += self.parent().retract(x.monomial_coefficient(m)*m) else: components[g_vect] = self.parent().retract(x.monomial_coefficient(m)*m) return components
def cone_points_iter(self): """ Iterate over the open torus orbits and yield distinct points. OUTPUT: For each open torus orbit (cone): A triple consisting of the cone, the nonzero homogeneous coordinates in that orbit (list of integers), and the nonzero log coordinates of distinct points as a cokernel. EXAMPLES:: sage: fan = NormalFan(ReflexivePolytope(2, 0)) sage: X = ToricVariety(fan, base_ring=GF(7)) sage: point_set = X.point_set() sage: ffe = point_set._finite_field_enumerator() sage: cpi = ffe.cone_points_iter() sage: cone, nonzero_points, cokernel = list(cpi)[5] sage: cone 1-d cone of Rational polyhedral fan in 2-d lattice N sage: cone.ambient_ray_indices() (2,) sage: nonzero_points [0, 1] sage: cokernel Finitely generated module V/W over Integer Ring with invariants (2) sage: list(cokernel) [(0), (1)] sage: [p.lift() for p in cokernel] [(0, 0), (0, 1)] """ from sage.matrix.constructor import matrix, block_matrix, identity_matrix from sage.rings.all import ZZ nrays = len(self.rays()) N = self.multiplicative_group_order() # Want cokernel of the log rescalings in (ZZ/N)^(#rays). But # ZZ/N is not a integral domain. Instead: work over ZZ log_generators = self.rescaling_log_generators() log_relations = block_matrix(2, 1, [ matrix(ZZ, len(log_generators), nrays, log_generators), N * identity_matrix(ZZ, nrays) ]) for cone in self.cone_iter(): nrays = self.fan().nrays() + len(self.fan().virtual_rays()) nonzero_coordinates = [ i for i in range(nrays) if i not in cone.ambient_ray_indices() ] log_relations_nonzero = log_relations.matrix_from_columns( nonzero_coordinates) image = log_relations_nonzero.image() cokernel = image.ambient_module().quotient(image) yield cone, nonzero_coordinates, cokernel
def lift(A, N): r""" Lift a matrix A from SL_m(Z/NZ) to SL_m(Z). Follows Shimura, Lemma 1.38, p21. """ assert A.is_square() assert A.determinant() != 0 m = A.nrows() if m == 1: return identity_matrix(1) D, U, V = A.smith_form() if U.determinant() == -1: U = matrix(2, 2, [-1, 0, 0, 1]) * U if V.determinant() == -1: V = V * matrix(2, 2, [-1, 0, 0, 1]) D = U * A * V assert U.determinant() == 1 assert V.determinant() == 1 a = [D[i, i] for i in range(m)] b = prod(a[1:]) W = identity_matrix(m) W[0, 0] = b W[1, 0] = b - 1 W[0, 1] = 1 X = identity_matrix(m) X[0, 1] = -a[1] Ap = D.parent()(D) Ap[0, 0] = 1 Ap[1, 0] = 1 - a[0] Ap[1, 1] *= a[0] assert (W * U * A * V * X).change_ring(Zmod(N)) == Ap.change_ring(Zmod(N)) Cp = diagonal_matrix(a[1:]) Cp[0, 0] *= a[0] C = lift(Cp, N) Cpp = block_diagonal_matrix(identity_matrix(1), C) Cpp[1, 0] = 1 - a[0] return (~U * ~W * Cpp * ~X * ~V).change_ring(ZZ)
def lift(A, N): r""" Lift a matrix A from SL_m(Z/NZ) to SL_m(Z). Follows Shimura, Lemma 1.38, p21. """ assert A.is_square() assert A.determinant() != 0 m = A.nrows() if m == 1: return identity_matrix(1) D, U, V = A.smith_form() if U.determinant() == -1 : U = matrix(2,2,[-1,0,0,1])* U if V.determinant() == -1 : V = V *matrix(2,2,[-1,0,0,1]) D = U*A*V assert U.determinant() == 1 assert V.determinant() == 1 a = [ D[i, i] for i in range(m) ] b = prod(a[1:]) W = identity_matrix(m) W[0, 0] = b W[1, 0] = b-1 W[0, 1] = 1 X = identity_matrix(m) X[0, 1] = -a[1] Ap = D.parent()(D) Ap[0, 0] = 1 Ap[1, 0] = 1-a[0] Ap[1, 1] *= a[0] assert (W*U*A*V*X).change_ring(Zmod(N)) == Ap.change_ring(Zmod(N)) Cp = diagonal_matrix(a[1:]) Cp[0, 0] *= a[0] C = lift(Cp, N) Cpp = block_diagonal_matrix(identity_matrix(1), C) Cpp[1, 0] = 1-a[0] return (~U * ~W * Cpp * ~X * ~V).change_ring(ZZ)
def cone_points_iter(self): """ Iterate over the open torus orbits and yield distinct points. OUTPUT: For each open torus orbit (cone): A triple consisting of the cone, the nonzero homogeneous coordinates in that orbit (list of integers), and the nonzero log coordinates of distinct points as a cokernel. EXAMPLES:: sage: fan = NormalFan(ReflexivePolytope(2, 0)) sage: X = ToricVariety(fan, base_ring=GF(7)) sage: point_set = X.point_set() sage: ffe = point_set._finite_field_enumerator() sage: cpi = ffe.cone_points_iter() sage: cone, nonzero_points, cokernel = list(cpi)[5] sage: cone 1-d cone of Rational polyhedral fan in 2-d lattice N sage: cone.ambient_ray_indices() (2,) sage: nonzero_points [0, 1] sage: cokernel Finitely generated module V/W over Integer Ring with invariants (2) sage: list(cokernel) [(0), (1)] sage: [p.lift() for p in cokernel] [(0, 0), (0, 1)] """ from sage.matrix.constructor import matrix, block_matrix, identity_matrix from sage.rings.all import ZZ nrays = len(self.rays()) N = self.multiplicative_group_order() # Want cokernel of the log rescalings in (ZZ/N)^(#rays). But # ZZ/N is not a integral domain. Instead: work over ZZ log_generators = self.rescaling_log_generators() log_relations = block_matrix(2, 1, [ matrix(ZZ, len(log_generators), nrays, log_generators), N * identity_matrix(ZZ, nrays)]) for cone in self.cone_iter(): nrays = self.fan().nrays() + len(self.fan().virtual_rays()) nonzero_coordinates = [i for i in range(nrays) if i not in cone.ambient_ray_indices()] log_relations_nonzero = log_relations.matrix_from_columns(nonzero_coordinates) image = log_relations_nonzero.image() cokernel = image.ambient_module().quotient(image) yield cone, nonzero_coordinates, cokernel
def analytic_continuation(ctx, ini=None, post=None): """ INPUT: - ``ini`` (constant matrix, optional) - initial values, one column per solution - ``post`` (matrix of polynomial/rational functions, optional) - linear combinations of the first Taylor coefficients to take, as a function of the evaluation point TESTS:: sage: from ore_algebra import DifferentialOperators sage: _, x, Dx = DifferentialOperators() sage: (Dx^2 + 2*x*Dx).numerical_solution([0, 2/sqrt(pi)], [0,i]) [+/- ...] + [1.65042575879754...]*I """ logger.info("path: %s", ctx.path) eps1 = (ctx.eps / (1 + len(ctx.path))) >> 2 # TBI, +: move to ctx? prec = utilities.prec_from_eps(eps1) if ini is not None: if not isinstance(ini, Matrix): # should this be here? try: ini = matrix(ctx.dop.order(), 1, list(ini)) except (TypeError, ValueError): raise ValueError("incorrect initial values: {}".format(ini)) try: ini = ini.change_ring(RealBallField(prec)) except (TypeError, ValueError): ini = ini.change_ring(ComplexBallField(prec)) res = [] path_mat = identity_matrix(ZZ, ctx.dop.order()) def store_value_if_wanted(point): if point.options.get('keep_value'): value = path_mat if ini is not None: value = value * ini if post is not None: value = post(point.value) * value res.append((point.value, value)) store_value_if_wanted(ctx.path.vert[0]) for step in ctx.path: step_mat = step_transition_matrix(step, eps1, ctx=ctx) path_mat = step_mat * path_mat store_value_if_wanted(step.end) cm = sage.structure.element.get_coercion_model() OutputIntervals = cm.common_parent( utilities.ball_field(ctx.eps, ctx.real()), *[mat.base_ring() for pt, mat in res]) return [(pt, mat.change_ring(OutputIntervals)) for pt, mat in res]
def permutation_simplicial_action(r, u, n, w): r""" From a word in 'l','r' return the simplicial action on homology as well as the obtained origami. INPUT: - ``r`` and ``u`` - permutations - ``n`` - the degree of the permutations - ``w`` - a string in 'l' and 'r' or a list of 2-tuples which are made of the letter 'l' or 'r' and a positive integer """ if w is None: w = [] elif isinstance(w, list): w = flatten_word(w) res = identity_matrix(2 * n) for letter in reversed(w): if letter == 'l': u = u * ~r m = identity_matrix(2 * n) m[:n, n:] = u.matrix() res = m * res elif letter == 'r': r = r * ~u m = identity_matrix(2 * n) m[n:, :n] = r.matrix() res = m * res else: raise ValueError, "does not understand the letter %s" % str(letter) return r, u, res
def permutation_simplicial_action(r,u,n,w): r""" From a word in 'l','r' return the simplicial action on homology as well as the obtained origami. INPUT: - ``r`` and ``u`` - permutations - ``n`` - the degree of the permutations - ``w`` - a string in 'l' and 'r' or a list of 2-tuples which are made of the letter 'l' or 'r' and a positive integer """ if w is None: w = [] elif isinstance(w,list): w = flatten_word(w) res = identity_matrix(2*n) for letter in reversed(w): if letter == 'l': u = u*~r m = identity_matrix(2*n) m[:n,n:] = u.matrix() res = m * res elif letter == 'r': r = r*~u m = identity_matrix(2*n) m[n:,:n] = r.matrix() res = m * res else: raise ValueError, "does not understand the letter %s" %str(letter) return r,u,res
def homogeneous_components(self): deg_matrix = block_matrix( [[identity_matrix(self.parent().rk), -self.parent()._B0]]) components = dict() x = self.lift() monomials = x.monomials() for m in monomials: g_vect = tuple(deg_matrix * vector(m.exponents()[0])) if g_vect in components: components[g_vect] += self.parent().retract( x.monomial_coefficient(m) * m) else: components[g_vect] = self.parent().retract( x.monomial_coefficient(m) * m) return components
def construct_from_dim_degree(dim, max_degree, base_ring, check): """ Construct a filtered vector space. INPUT: - ``dim`` -- integer. The dimension. - ``max_degree`` -- integer or infinity. The maximal degree where the vector subspace of the filtration is still the entire space. EXAMPLES:: sage: V = FilteredVectorSpace(2, 5); V QQ^2 >= 0 sage: V.get_degree(5) Vector space of degree 2 and dimension 2 over Rational Field Basis matrix: [1 0] [0 1] sage: V.get_degree(6) Vector space of degree 2 and dimension 0 over Rational Field Basis matrix: [] sage: FilteredVectorSpace(2, oo) QQ^2 sage: FilteredVectorSpace(2, -oo) 0 in QQ^2 TESTS:: sage: from sage.modules.filtered_vector_space import construct_from_dim_degree sage: V = construct_from_dim_degree(2, 5, QQ, True); V QQ^2 >= 0 """ if dim not in ZZ: raise ValueError('dimension must be an integer') dim = ZZ(dim) from sage.matrix.constructor import identity_matrix generators = identity_matrix(base_ring, dim).columns() filtration = dict() if max_degree is None: max_degree = infinity filtration[normalize_degree(max_degree)] = range(dim) return construct_from_generators_indices(generators, filtration, base_ring, check)
def gens(self): """ Return the generators. OUTPUT: A tuple of linear expressions, one for each linear variable. EXAMPLES:: sage: from sage.geometry.linear_expression import LinearExpressionModule sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z')) sage: L.gens() (x + 0*y + 0*z + 0, 0*x + y + 0*z + 0, 0*x + 0*y + z + 0) """ from sage.matrix.constructor import identity_matrix identity = identity_matrix(self.base_ring(), self.ngens()) return tuple(self(e, 0) for e in identity.rows())
def gens(self): """ Return the generators of ``self``. OUTPUT: A tuple of linear expressions, one for each linear variable. EXAMPLES:: sage: from sage.geometry.linear_expression import LinearExpressionModule sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z')) sage: L.gens() (x + 0*y + 0*z + 0, 0*x + y + 0*z + 0, 0*x + 0*y + z + 0) """ from sage.matrix.constructor import identity_matrix identity = identity_matrix(self.base_ring(), self.ngens()) return tuple(self(e, 0) for e in identity.rows())
def __init__(self, Prg, Rng, H = [], Settings = {'Iterations':150,'detail':True,'tryKKT':0, 'AutoMatrix':True, 'precision':7}): f = Prg[0] self.Prog = [] self.PolyRng = Rng self.VARS = self.PolyRng.gens() self.polynomial = f self.Field = self.polynomial.base_ring() self.number_of_variables = len(self.VARS) self.total_degree = f.total_degree() self.constant_term = f.constant_coefficient() self.prg_size = 1 if len(Prg) > 1: self.prg_size = len(Prg[1]) + 1 if H == []: self.H = identity_matrix(self.Field, self.prg_size) else: self.H = H self.Prog.append(f) self.constant_terms.append(f.constant_coefficient()) for i in range(self.prg_size - 1): self.Prog.append(Prg[1][i]) self.constant_terms.append(Prg[1][i].constant_coefficient()) tmp_deg = Prg[1][i].total_degree() if self.total_degree < tmp_deg: self.total_degree = tmp_deg self.Settings = Settings if 'AutoMatrix' in Settings: self.AutoMatrix = Settings['AutoMatrix'] else: self.AutoMatrix = False if 'Order' in Settings: self.Ord = max(Settings['Order'], self.total_degree) else: self.Ord = self.total_degree if (self.Ord % 2) == 1: self.Ord = self.Ord + 1 self.MarginalCoeffs = [] if 'precision' in Settings: self.error_bound = 10**(-Settings['precision']) else: self.error_bound = 10**(-7) self.Info = {'gp':0,'CPU':0,'Wall':0,'status':'Unknown','Message':''}
def invariant_bilinear_form(self): """ Return the symmetric bilinear form preserved by ``self``. OUTPUT: A matrix. EXAMPLES:: sage: GO(2,3,+1).invariant_bilinear_form() [0 1] [1 0] sage: GO(2,3,-1).invariant_bilinear_form() [2 1] [1 1] sage: G = GO(4, QQ) sage: G.invariant_bilinear_form() [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] sage: GO3m = GO(3,QQ, invariant_form=(1,0,0,0,2,0,0,0,3)) sage: GO3m.invariant_bilinear_form() [1 0 0] [0 2 0] [0 0 3] TESTS:: sage: GO3m.invariant_form() [1 0 0] [0 2 0] [0 0 3] """ if self._invariant_form is not None: return self._invariant_form from sage.matrix.constructor import identity_matrix m = identity_matrix(self.base_ring(), self.degree()) m.set_immutable() return m
def cartan_matrix(self): r""" Returns the Cartan matrix for this Dynkin diagram EXAMPLES:: sage: DynkinDiagram(['C',3]).cartan_matrix() [ 2 -1 0] [-1 2 -2] [ 0 -1 2] """ from sage.matrix.constructor import identity_matrix from sage.rings.all import ZZ index_set = self.index_set() reverse = dict((index_set[i], i) for i in range(len(index_set))) m = 2*identity_matrix(ZZ, len(self.index_set()), sparse=True) for (i,j,l) in self.edge_iterator(): m[reverse[j], reverse[i]] = -l m.set_immutable() return m
def invariant_quadratic_form(self): """ Return the quadratic form preserved by the orthogonal group. OUTPUT: A matrix. EXAMPLES:: sage: GO(2,3,+1).invariant_quadratic_form() [0 1] [0 0] sage: GO(2,3,-1).invariant_quadratic_form() [1 1] [0 2] """ from sage.matrix.constructor import identity_matrix m = identity_matrix(self.base_ring(), self.degree()) m.set_immutable() return m
def _normalisation_factor_zz(self, tau=3): r""" This function returns an approximation of `∑_{x ∈ \ZZ^n} \exp(-|x|_2^2/(2σ²))`, i.e. the normalisation factor such that the sum over all probabilities is 1 for `\ZZⁿ`. If this ``self.B`` is not an identity matrix over `\ZZ` a ``NotImplementedError`` is raised. INPUT: - ``tau`` -- all vectors `v` with `|v|_∞ ≤ τ·σ` are enumerated (default: ``3``). EXAMPLE:: sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: n = 3; sigma = 1.0; m = 1000 sage: D = DiscreteGaussianDistributionLatticeSampler(ZZ^n, sigma) sage: f = D.f sage: c = D._normalisation_factor_zz(); c 15.528... sage: l = [D() for _ in xrange(m)] sage: v = vector(ZZ, n, (0, 0, 0)) sage: l.count(v), ZZ(round(m*f(v)/c)) (57, 64) """ if self.B != identity_matrix(ZZ, self.B.nrows()): raise NotImplementedError( "This function is only implemented when B is an identity matrix." ) f = self.f n = self.B.ncols() sigma = self._sigma return sum( f(x) for x in _iter_vectors(n, -ceil(tau * sigma), ceil(tau * sigma)))
def hnf_with_transformation(A, proof=True): """ Compute the HNF H of A along with a transformation matrix U such that U*A = H. Also return the pivots of H. INPUT: - A -- an n x m matrix A over the integers. - proof -- whether or not to prove the result correct. OUTPUT: - matrix -- the Hermite normal form H of A - U -- a unimodular matrix such that U * A = H - pivots -- the pivot column positions of A EXAMPLES:: sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf sage: A = matrix(ZZ, 2, [1, -5, -10, 1, 3, 197]); A [ 1 -5 -10] [ 1 3 197] sage: H, U, pivots = matrix_integer_dense_hnf.hnf_with_transformation(A) sage: H [ 1 3 197] [ 0 8 207] sage: U [ 0 1] [-1 1] sage: U*A [ 1 3 197] [ 0 8 207] """ # All we do is augment the input matrix with the identity matrix of the appropriate rank on the right. C = A.augment(identity_matrix(ZZ, A.nrows())) H, pivots = hnf(C, include_zero_rows=True, proof=proof) U = H.matrix_from_columns(range(A.ncols(), H.ncols())) H2 = H.matrix_from_columns(range(A.ncols())) return H2, U, pivots
def fundamental_matrix_ordinary(dop, pt, eps, rows, maj, fail_fast): eps_col = bounds.IR(eps) / bounds.IR(dop.order()).sqrt() eps_col = accuracy.AbsoluteError(eps_col) evpt = EvaluationPoint(pt, jet_order=rows) bwrec = bw_shift_rec(dop) inis = [ LogSeriesInitialValues(ZZ.zero(), ini, dop, check=False) for ini in identity_matrix(dop.order()) ] stop = accuracy.StoppingCriterion(maj, eps_col.eps) cols = [ interval_series_sum_wrapper(series_sum_ordinary, dop, ini, evpt, eps_col, bwrec, stop, fail_fast, max_prec=None) for ini in inis ] return matrix(cols).transpose()
def identity(self): r""" Return identity morphism in an endomorphism ring. EXAMPLES:: sage: V=FreeModule(ZZ,5) sage: H=V.Hom(V) sage: H.identity() Free module morphism defined by the matrix [1 0 0 0 0] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 1 0] [0 0 0 0 1] Domain: Ambient free module of rank 5 over the principal ideal domain ... Codomain: Ambient free module of rank 5 over the principal ideal domain ... """ if self.is_endomorphism_set(): return self(identity_matrix(self.base_ring(),self.domain().rank())) else: raise TypeError("Identity map only defined for endomorphisms. Try natural_map() instead.")
def generator_matrix(self): r""" Return a generator matrix of ``self``. EXAMPLES:: sage: C = codes.ParityCheckCode(GF(5),7) sage: E = codes.encoders.ParityCheckCodeGeneratorMatrixEncoder(C) sage: E.generator_matrix() [1 0 0 0 0 0 0 4] [0 1 0 0 0 0 0 4] [0 0 1 0 0 0 0 4] [0 0 0 1 0 0 0 4] [0 0 0 0 1 0 0 4] [0 0 0 0 0 1 0 4] [0 0 0 0 0 0 1 4] """ k = self.code().dimension() field = self.code().base_field() G = identity_matrix(field, k) G = G.augment(vector(field, [-field.one()] * k)) G.set_immutable() return G
def _Hasse_Witt_cached(self): r""" INPUT: - ``E`` : Hyperelliptic Curve of the form `y^2 = f(x)` over a finite field, `\mathbb{F}_q` OUTPUT: - ``N`` : The matrix `N = M M^p \dots M^{p^{g-1}}` where `M = c_{pi-j}, f(x)^{(p-1)/2} = \sum c_i x^i` - ``E`` : The initial curve to check some caching conditions. EXAMPLES:: sage: K.<x>=GF(9,'x')[] sage: C=HyperellipticCurve(x^7-1,0) sage: C._Hasse_Witt_cached() ( [0 0 0] [0 0 0] [0 0 0], Hyperelliptic Curve over Finite Field in x of size 3^2 defined by y^2 = x^7 + 2 ) sage: K.<x>=GF(49,'x')[] sage: C=HyperellipticCurve(x^5+1,0) sage: C._Hasse_Witt_cached() ( [0 0] [0 0], Hyperelliptic Curve over Finite Field in x of size 7^2 defined by y^2 = x^5 + 1 ) sage: P.<x>=GF(9,'a')[] sage: C=HyperellipticCurve(x^29+1,0) sage: C._Hasse_Witt_cached() ( [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0], Hyperelliptic Curve over Finite Field in a of size 3^2 defined by y^2 = x^29 + 1 ) """ # If Cartier Matrix is already cached for this curve, use that or evaluate it to get M, #Coeffs, genus, Fq=base field of self, p=char(Fq). This is so we have one less matrix to #compute. #We use caching here since Cartier matrix is needed to compute Hasse Witt. So if the Cartier #is already computed it is stored in list A. If it was not cached (i.e. A is empty), we simply #compute it. If it is cached then we need to make sure that we have the correct one. So check #which curve does the matrix correspond to. Since caching stores a lot of stuff, we only check #the last entry in A. If it does not match, clear A and compute Cartier. # #Since Trac Ticket #11115, there is a different cache for methods #that don't accept arguments. Anyway, the easiest is to call #the cached method and simply see whether the data belong to self. M, Coeffs, g, Fq, p, E = self._Cartier_matrix_cached() if E != self: self._Cartier_matrix_cached.clear_cache() M, Coeffs, g, Fq, p, E = self._Cartier_matrix_cached() #This compute the action of p^kth Frobenius on list of coefficients def frob_mat(Coeffs, k): a = p**k mat = [] Coeffs_pow = [c**a for c in Coeffs] for i in range(1, g + 1): H = [(Coeffs[j]) for j in range((p * i - 1), (p * i - g - 1), -1)] mat.append(H) return matrix(Fq, mat) #Computes all the different possible action of frobenius on matrix M and stores in list Mall Mall = [M] + [frob_mat(Coeffs, k) for k in range(1, g)] #initial N=I, so we can go through Mall and multiply all matrices with I and #get the Hasse-Witt matrix. N = identity_matrix(Fq, g) for l in Mall: N = N * l return N, E
def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, \ quotient=None, dual=False, ntl=False): """ This function generates different types of integral lattice bases of row vectors relevant in cryptography. Randomness can be set either with ``seed``, or by using :func:`sage.misc.randstate.set_random_seed`. INPUT: * ``type`` - one of the following strings * ``'modular'`` (default). A class of lattices for which asymptotic worst-case to average-case connections hold. For more refer to [A96]_. * ``'random'`` - Special case of modular (n=1). A dense class of lattice used for testing basis reduction algorithms proposed by Goldstein and Mayer [GM02]_. * ``'ideal'`` - Special case of modular. Allows for a more compact representation proposed by [LM06]_. * ``'cyclotomic'`` - Special case of ideal. Allows for efficient processing proposed by [LM06]_. * ``n`` - Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. * ``m`` - Lattice dimension, `L \subseteq Z^m`. * ``q`` - Coefficent size, `q*Z^m \subseteq L`. * ``seed`` - Randomness seed. * ``quotient`` - For the type ideal, this determines the quotient polynomial. Ignored for all other types. * ``dual`` - Set this flag if you want a basis for `q*dual(L)`, for example for Regev's LWE bases [R05]_. * ``ntl`` - Set this flag if you want the lattice basis in NTL readable format. OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: * Modular basis :: sage: sage.crypto.gen_lattice(m=10, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] * Random basis :: sage: sage.crypto.gen_lattice(type='random', n=1, m=10, q=11^4, seed=42) [14641 0 0 0 0 0 0 0 0 0] [ 431 1 0 0 0 0 0 0 0 0] [-4792 0 1 0 0 0 0 0 0 0] [ 1015 0 0 1 0 0 0 0 0 0] [-3086 0 0 0 1 0 0 0 0 0] [-5378 0 0 0 0 1 0 0 0 0] [ 4769 0 0 0 0 0 1 0 0 0] [-1159 0 0 0 0 0 0 1 0 0] [ 3082 0 0 0 0 0 0 0 1 0] [-4580 0 0 0 0 0 0 0 0 1] * Ideal bases with quotient x^n-1, m=2*n are NTRU bases :: sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [-3 4 -2 -3 0 1 0 0] [-3 -3 4 -2 0 0 1 0] [-2 -3 -3 4 0 0 0 1] * Cyclotomic bases with n=2^k are SWIFFT bases :: sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [ 3 4 -2 -3 0 1 0 0] [ 3 3 4 -2 0 0 1 0] [ 2 3 3 4 0 0 0 1] * Dual modular bases are related to Regev's famous public-key encryption [R05]_ :: sage: sage.crypto.gen_lattice(type='modular', m=10, seed=42, dual=True) [ 0 0 0 0 0 0 0 0 0 11] [ 0 0 0 0 0 0 0 0 11 0] [ 0 0 0 0 0 0 0 11 0 0] [ 0 0 0 0 0 0 11 0 0 0] [ 0 0 0 0 0 11 0 0 0 0] [ 0 0 0 0 11 0 0 0 0 0] [ 0 0 0 1 -5 -2 -1 1 -3 5] [ 0 0 1 0 -3 4 1 4 -3 -2] [ 0 1 0 0 -4 5 -3 3 5 3] [ 1 0 0 0 -2 -1 4 2 5 4] * Relation of primal and dual bases :: sage: B_primal=sage.crypto.gen_lattice(m=10, q=11, seed=42) sage: B_dual=sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) sage: B_dual_alt=transpose(11*B_primal.inverse()).change_ring(ZZ) sage: B_dual_alt.hermite_form() == B_dual.hermite_form() True REFERENCES: .. [A96] Miklos Ajtai. Generating hard instances of lattice problems (extended abstract). STOC, pp. 99--108, ACM, 1996. .. [GM02] Daniel Goldstein and Andrew Mayer. On the equidistribution of Hecke points. Forum Mathematicum, 15:2, pp. 165--189, De Gruyter, 2003. .. [LM06] Vadim Lyubashevsky and Daniele Micciancio. Generalized compact knapsacks are collision resistant. ICALP, pp. 144--155, Springer, 2006. .. [R05] Oded Regev. On lattices, learning with errors, random linear codes, and cryptography. STOC, pp. 84--93, ACM, 2005. """ from sage.rings.finite_rings.integer_mod_ring \ import IntegerModRing from sage.matrix.constructor import matrix, \ identity_matrix, block_matrix from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import IntegerRing if seed != None: from sage.misc.randstate import set_random_seed set_random_seed(seed) if type == 'random': if n != 1: raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) A = identity_matrix(ZZ_q, n) if type == 'random' or type == 'modular': R = MatrixSpace(ZZ_q, m-n, n) A = A.stack(R.random_element()) elif type == 'ideal': if quotient == None: raise \ ValueError('ideal bases require a quotient polynomial') x = quotient.default_variable() if n != quotient.degree(x): raise \ ValueError('ideal bases require n = quotient.degree()') R = ZZ_q[x].quotient(quotient, x) for i in range(m//n): A = A.stack(R.random_element().matrix()) elif type == 'cyclotomic': from sage.rings.arith import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2*n,n,-1): if euler_phi(k) == n: found = True break if not found: raise \ ValueError('cyclotomic bases require that n is an image of' + \ 'Euler\'s totient function') R = ZZ_q['x'].quotient(cyclotomic_polynomial(k, 'x'), 'x') for i in range(m//n): A = A.stack(R.random_element().matrix()) # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): if abs(a-q) < abs(a): return a-q else: return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: B = block_matrix([[ZZ(q), ZZ.zero()], [A_prime, ZZ.one()] ], \ subdivide=False) else: B = block_matrix([[ZZ.one(), -A_prime.transpose()], [ZZ.zero(), \ ZZ(q)]], subdivide=False) for i in range(m//2): B.swap_rows(i,m-i-1) if not ntl: return B else: return B._ntl_()
def saturation(A, proof=True, p=0, max_dets=5): """ Compute a saturation matrix of A. INPUT: - A -- a matrix over ZZ - proof -- bool (default: True) - p -- int (default: 0); if not 0 only guarantees that output is p-saturated - max_dets -- int (default: 4) max number of dets of submatrices to compute. OUTPUT: matrix -- saturation of the matrix A. EXAMPLES:: sage: from sage.matrix.matrix_integer_dense_saturation import saturation sage: A = matrix(ZZ, 2, 2, [3,2,3,4]); B = matrix(ZZ, 2,3,[1,2,3,4,5,6]); C = A*B sage: C [11 16 21] [19 26 33] sage: C.index_in_saturation() 18 sage: S = saturation(C); S [11 16 21] [-2 -3 -4] sage: S.index_in_saturation() 1 sage: saturation(C, proof=False) [11 16 21] [-2 -3 -4] sage: saturation(C, p=2) [11 16 21] [-2 -3 -4] sage: saturation(C, p=2, max_dets=1) [11 16 21] [-2 -3 -4] """ # Find a submatrix of full rank and instead saturate that matrix. r = A.rank() if A.is_square() and r == A.nrows(): return identity_matrix(ZZ, r) if A.nrows() > r: P = [] while len(P) < r: P = matrix_integer_dense_hnf.probable_pivot_rows(A) A = A.matrix_from_rows(P) # Factor out all common factors from all rows, just in case. A = copy(A) A._factor_out_common_factors_from_each_row() if A.nrows() <= 1: return A A, zero_cols = A._delete_zero_columns() if max_dets > 0: # Take the GCD of at most num_dets randomly chosen determinants. nr = A.nrows() nc = A.ncols() d = 0 trials = min(binomial(nc, nr), max_dets) already_tried = [] while len(already_tried) < trials: v = random_sublist_of_size(nc, nr) tm = verbose('saturation -- checking det condition on submatrix') d = gcd(d, A.matrix_from_columns(v).determinant(proof=proof)) verbose('saturation -- got det down to %s' % d, tm) if gcd(d, p) == 1: return A._insert_zero_columns(zero_cols) already_tried.append(v) if gcd(d, p) == 1: # already p-saturated return A._insert_zero_columns(zero_cols) # Factor and p-saturate at each p. # This is not a good algorithm, because all the HNF's in it are really slow! # #tm = verbose('factoring gcd %s of determinants'%d) #limit = 2**31-1 #F = d.factor(limit = limit) #D = [p for p, e in F if p <= limit] #B = [n for n, e in F if n > limit] # all big factors -- there will only be at most one #assert len(B) <= 1 #C = B[0] #for p in D: # A = p_saturation(A, p=p, proof=proof) # This is a really simple but powerful algorithm. # FACT: If A is a matrix of full rank, then hnf(transpose(A))^(-1)*A is a saturation of A. # To make this practical we use solve_system_with_difficult_last_row, since the # last column of HNF's are typically the only really big ones. B = A.transpose().hermite_form(include_zero_rows=False, proof=proof) B = B.transpose() # Now compute B^(-1) * A C = solve_system_with_difficult_last_row(B, A) return C.change_ring(ZZ)._insert_zero_columns(zero_cols)
def simplicial_action_generators(self): r""" Return action of generators of the Veech group on homology """ from sage.matrix.constructor import matrix, identity_matrix tree, reps, word_reps, gens = self._veech_group._spanning_tree_verrill( on_right=False) n = self[0].nb_squares() l_mat = identity_matrix(2 * n) s_mat = matrix(2 * n) s_mat[:n, n:] = -identity_matrix(n) reps = [None] * len(tree) reps_o = [None] * len(tree) reps[0] = identity_matrix(2 * n) reps_o[0] = self.origami() waiting = [0] while waiting: x = waiting.pop() for (e0, e1, label) in tree.outgoing_edges(x): waiting.append(e1) o = reps_o[e0] if label == 's': m = copy(s_mat) m[n:, :n] = o.u().matrix().transpose() reps[e1] = m * reps[e0] reps_o[e1] = o.S_action() elif label == 'l': o = o.L_action() m = copy(l_mat) m[:n, n:] = (~o.u()).matrix().transpose() reps[e1] = m * reps[e0] reps_o[e1] = o elif label == 'r': o = o.R_action() m = copy(l_mat) m[n:, :n] = (~o.r()).matrix().transpose() reps[e1] = m * reps[e0] reps_o[e1] = o else: raise ValueError("does know how to do only with slr") m_gens = [] for (e0, e1, label) in gens: o = reps_o[e0] if label == 's': oo = o.S_action() m = copy(s_mat) m[n:, :n] = o.u().matrix().transpose() elif label == 'l': oo = o.L_action() m = copy(l_mat) m[:n, n:] = (~oo.u()).matrix().transpose() elif label == 'r': oo = o.R_action() m = copy(l_mat) m[:n, n:] = (~oo.r()).matrix().transpose() else: raise ValueError("does know how to do only with slr") ooo = reps_o[e1] ot, oo_renum = oo.standard_form(True) os, ooo_renum = ooo.standard_form(True) assert (ot == os) m_ren = (oo_renum * ~ooo_renum).matrix().transpose() m_s = matrix(2 * n) m_s[:n, :n] = m_ren m_s[n:, n:] = m_ren m_gens.append(~reps[e1] * m_s * m * reps[e0]) return tree, reps, reps_o, gens, m_gens, word_reps
def multiplier(self, P, n, check=True): r""" Returns the multiplier of the point ``P`` of period ``n`` by the map. The map must be an endomorphism. INPUT: - ``P`` - a point on domain of the map. - ``n`` - a positive integer, the period of ``P``. - ``check`` -- verify that ``P`` has period ``n``, Default:True. OUTPUT: - a square matrix of size ``self.codomain().dimension_relative()`` in the ``base_ring`` of the map. EXAMPLES:: sage: P.<x,y> = AffineSpace(QQ, 2) sage: H = End(P) sage: f = H([x^2, y^2]) sage: f.multiplier(P([1, 1]), 1) [2 0] [0 2] :: sage: P.<x,y,z> = AffineSpace(QQ, 3) sage: H = End(P) sage: f = H([x, y^2, z^2 - y]) sage: f.multiplier(P([1/2, 1, 0]), 2) [1 0 0] [0 4 0] [0 0 0] :: sage: P.<x> = AffineSpace(CC, 1) sage: H = End(P) sage: f = H([x^2 + 1/2]) sage: f.multiplier(P([0.5 + 0.5*I]), 1) [1.00000000000000 + 1.00000000000000*I] :: sage: R.<t> = PolynomialRing(CC, 1) sage: P.<x> = AffineSpace(R, 1) sage: H = End(P) sage: f = H([x^2 - t^2 + t]) sage: f.multiplier(P([-t + 1]), 1) [(-2.00000000000000)*t + 2.00000000000000] :: sage: P.<x,y> = AffineSpace(QQ, 2) sage: X = P.subscheme([x^2-y^2]) sage: H = End(X) sage: f = H([x^2, y^2]) sage: f.multiplier(X([1, 1]), 1) [2 0] [0 2] """ if not self.is_endomorphism(): raise TypeError("must be an endomorphism") if check: if self.nth_iterate(P, n) != P: raise ValueError("%s is not periodic of period %s" % (P, n)) if n < 1: raise ValueError("period must be a positive integer") N = self.domain().ambient_space().dimension_relative() l = identity_matrix(FractionField(self.codomain().base_ring()), N, N) Q = P J = self.jacobian() for i in range(0, n): R = self(Q) l = J(tuple(Q)) * l #chain rule matrix multiplication Q = R return l
def calculate_voronoi_cell(basis, radius=None, verbose=False): """ Calculate the Voronoi cell of the lattice defined by basis INPUT: - ``basis`` -- embedded basis matrix of the lattice - ``radius`` -- radius of basis vectors to consider - ``verbose`` -- whether to print debug information OUTPUT: A :class:`Polyhedron` instance. EXAMPLES:: sage: from sage.modules.diamond_cutting import calculate_voronoi_cell sage: V = calculate_voronoi_cell(matrix([[1, 0], [0, 1]])) sage: V.volume() 1 """ dim = basis.dimensions() artificial_length = None if dim[0] < dim[1]: # introduce "artificial" basis points (representing infinity) artificial_length = max(abs(v) for v in basis).ceil() * 2 additional_vectors = identity_matrix(dim[1]) * artificial_length basis = basis.stack(additional_vectors) # LLL-reduce to get quadratic matrix basis = basis.LLL() basis = matrix([v for v in basis if v]) dim = basis.dimensions() if dim[0] != dim[1]: raise ValueError("invalid matrix") basis = basis / 2 ieqs = [] for v in basis: ieqs.append(plane_inequality(v)) ieqs.append(plane_inequality(-v)) Q = Polyhedron(ieqs=ieqs) # twice the length of longest vertex in Q is a safe choice if radius is None: radius = 2 * max(abs(v)**2 for v in basis) V = diamond_cut(Q, basis, radius, verbose=verbose) if artificial_length is not None: # remove inequalities introduced by artificial basis points H = V.Hrepresentation() H = [ v for v in H if all(not V._is_zero(v.A() * w / 2 - v.b()) and not V._is_zero(v.A() * (-w) / 2 - v.b()) for w in additional_vectors) ] V = Polyhedron(ieqs=H) return V
def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, quotient=None, dual=False, ntl=False, lattice=False): r""" This function generates different types of integral lattice bases of row vectors relevant in cryptography. Randomness can be set either with ``seed``, or by using :func:`sage.misc.randstate.set_random_seed`. INPUT: - ``type`` -- one of the following strings - ``'modular'`` (default) -- A class of lattices for which asymptotic worst-case to average-case connections hold. For more refer to [Aj1996]_. - ``'random'`` -- Special case of modular (n=1). A dense class of lattice used for testing basis reduction algorithms proposed by Goldstein and Mayer [GM2002]_. - ``'ideal'`` -- Special case of modular. Allows for a more compact representation proposed by [LM2006]_. - ``'cyclotomic'`` -- Special case of ideal. Allows for efficient processing proposed by [LM2006]_. - ``n`` -- Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. - ``m`` -- Lattice dimension, `L \subseteq Z^m`. - ``q`` -- Coefficient size, `q-Z^m \subseteq L`. - ``seed`` -- Randomness seed. - ``quotient`` -- For the type ideal, this determines the quotient polynomial. Ignored for all other types. - ``dual`` -- Set this flag if you want a basis for `q-dual(L)`, for example for Regev's LWE bases [Reg2005]_. - ``ntl`` -- Set this flag if you want the lattice basis in NTL readable format. - ``lattice`` -- Set this flag if you want a :class:`FreeModule_submodule_with_basis_integer` object instead of an integer matrix representing the basis. OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: Modular basis:: sage: sage.crypto.gen_lattice(m=10, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] Random basis:: sage: sage.crypto.gen_lattice(type='random', n=1, m=10, q=11^4, seed=42) [14641 0 0 0 0 0 0 0 0 0] [ 431 1 0 0 0 0 0 0 0 0] [-4792 0 1 0 0 0 0 0 0 0] [ 1015 0 0 1 0 0 0 0 0 0] [-3086 0 0 0 1 0 0 0 0 0] [-5378 0 0 0 0 1 0 0 0 0] [ 4769 0 0 0 0 0 1 0 0 0] [-1159 0 0 0 0 0 0 1 0 0] [ 3082 0 0 0 0 0 0 0 1 0] [-4580 0 0 0 0 0 0 0 0 1] Ideal bases with quotient x^n-1, m=2*n are NTRU bases:: sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [-2 -3 -3 4 1 0 0 0] [ 4 -2 -3 -3 0 1 0 0] [-3 4 -2 -3 0 0 1 0] [-3 -3 4 -2 0 0 0 1] Ideal bases also work with polynomials:: sage: R.<t> = PolynomialRing(ZZ) sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=t^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 1 4 -3 3 1 0 0 0] [ 3 1 4 -3 0 1 0 0] [-3 3 1 4 0 0 1 0] [ 4 -3 3 1 0 0 0 1] Cyclotomic bases with n=2^k are SWIFFT bases:: sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [-2 -3 -3 4 1 0 0 0] [-4 -2 -3 -3 0 1 0 0] [ 3 -4 -2 -3 0 0 1 0] [ 3 3 -4 -2 0 0 0 1] Dual modular bases are related to Regev's famous public-key encryption [Reg2005]_:: sage: sage.crypto.gen_lattice(type='modular', m=10, seed=42, dual=True) [ 0 0 0 0 0 0 0 0 0 11] [ 0 0 0 0 0 0 0 0 11 0] [ 0 0 0 0 0 0 0 11 0 0] [ 0 0 0 0 0 0 11 0 0 0] [ 0 0 0 0 0 11 0 0 0 0] [ 0 0 0 0 11 0 0 0 0 0] [ 0 0 0 1 -5 -2 -1 1 -3 5] [ 0 0 1 0 -3 4 1 4 -3 -2] [ 0 1 0 0 -4 5 -3 3 5 3] [ 1 0 0 0 -2 -1 4 2 5 4] Relation of primal and dual bases:: sage: B_primal=sage.crypto.gen_lattice(m=10, q=11, seed=42) sage: B_dual=sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) sage: B_dual_alt=transpose(11*B_primal.inverse()).change_ring(ZZ) sage: B_dual_alt.hermite_form() == B_dual.hermite_form() True TESTS: Test some bad quotient polynomials:: sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=cos(x)) Traceback (most recent call last): ... TypeError: unable to convert cos(x) to an integer sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=x^23-1) Traceback (most recent call last): ... ValueError: ideal basis requires n = quotient.degree() sage: R.<u,v> = ZZ[] sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=u+v) Traceback (most recent call last): ... TypeError: quotient should be a univariate polynomial We are testing output format choices:: sage: sage.crypto.gen_lattice(m=10, q=11, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, ntl=True) [ [11 0 0 0 0 0 0 0 0 0] [0 11 0 0 0 0 0 0 0 0] [0 0 11 0 0 0 0 0 0 0] [0 0 0 11 0 0 0 0 0 0] [2 4 3 5 1 0 0 0 0 0] [1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] ] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) Free module of degree 10 and rank 10 over Integer Ring User basis matrix: [ 0 0 1 1 0 -1 -1 -1 1 0] [-1 1 0 1 0 1 1 0 1 1] [-1 0 0 0 -1 1 1 -2 0 0] [-1 -1 0 1 1 0 0 1 1 -1] [ 1 0 -1 0 0 0 -2 -2 0 0] [ 2 -1 0 0 1 0 1 0 0 -1] [-1 1 -1 0 1 -1 1 0 -1 -2] [ 0 0 -1 3 0 0 0 -1 -1 -1] [ 0 -1 0 -1 2 0 -1 0 0 2] [ 0 1 1 0 1 1 -2 1 -1 -2] """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.matrix.constructor import identity_matrix, block_matrix from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import IntegerRing if seed is not None: from sage.misc.randstate import set_random_seed set_random_seed(seed) if type == 'random': if n != 1: raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) A = identity_matrix(ZZ_q, n) if type == 'random' or type == 'modular': R = MatrixSpace(ZZ_q, m-n, n) A = A.stack(R.random_element()) elif type == 'ideal': if quotient is None: raise ValueError('ideal bases require a quotient polynomial') try: quotient = quotient.change_ring(ZZ_q) except (AttributeError, TypeError): quotient = quotient.polynomial(base_ring=ZZ_q) P = quotient.parent() # P should be a univariate polynomial ring over ZZ_q if not is_PolynomialRing(P): raise TypeError("quotient should be a univariate polynomial") assert P.base_ring() is ZZ_q if quotient.degree() != n: raise ValueError('ideal basis requires n = quotient.degree()') R = P.quotient(quotient) for i in range(m//n): A = A.stack(R.random_element().matrix()) elif type == 'cyclotomic': from sage.arith.all import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2*n,n,-1): if euler_phi(k) == n: found = True break if not found: raise ValueError("cyclotomic bases require that n " "is an image of Euler's totient function") R = ZZ_q['x'].quotient(cyclotomic_polynomial(k, 'x'), 'x') for i in range(m//n): A = A.stack(R.random_element().matrix()) # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): if abs(a-q) < abs(a): return a-q else: return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: B = block_matrix([[ZZ(q), ZZ.zero()], [A_prime, ZZ.one()] ], subdivide=False) else: B = block_matrix([[ZZ.one(), -A_prime.transpose()], [ZZ.zero(), ZZ(q)]], subdivide=False) for i in range(m//2): B.swap_rows(i,m-i-1) if ntl and lattice: raise ValueError("Cannot specify ntl=True and lattice=True " "at the same time") if ntl: return B._ntl_() elif lattice: from sage.modules.free_module_integer import IntegerLattice return IntegerLattice(B) else: return B