def compatibility_degree(self, alpha, beta): if self.is_finite(): tube_contribution = -1 elif self.is_affine(): gck = self.gamma().associated_coroot() if any([gck.scalar(alpha) != 0, gck.scalar(beta) != 0]): tube_contribution = -1 else: sup_a = self._tube_support(alpha) sup_b = self._tube_support(beta) if all([x in sup_b for x in sup_a]) or all([x in sup_a for x in sup_b]): tube_contribution = -1 else: nbh_a = self._tube_nbh(alpha) tube_contribution = len([ x for x in nbh_a if x in sup_b ]) else: raise ValueError("compatibility degree is implemented only for finite and affine types") initial = self.initial_cluster() if alpha in initial: return max(beta[initial.index(alpha)],0) alphacheck = alpha.associated_coroot() if beta in initial: return max(alphacheck[initial.index(beta)],0) Ap = -matrix(self.rk, map(lambda x: max(x,0), self.b_matrix().list() ) ) Am = matrix(self.rk, map(lambda x: min(x,0), self.b_matrix().list() ) ) a = vector(alphacheck) b = vector(beta) return max( -a*b-a*Am*b, -a*b-a*Ap*b, tube_contribution )
def semi_norm_v(M, v, p=2, verbose=False): r""" Return the semi norm on the hyperplane orthogonal to v. EXAMPLES:: sage: from slabbe.matrix_cocycle import semi_norm_v sage: A1 = matrix(3, [1,-1,-1, 0,1,0, 0,0,1]).inverse() sage: semi_norm_v(A1, vector( (1,1,1))) # tolerance 0.0001 0.9999999999890247 sage: semi_norm_v(A1, vector( (1,1,1)), p=1) # tolerance 0.0001 0.9999394820959548 sage: semi_norm_v(A1, vector( (1,1,1)), p=oo) # tolerance 0.0001 1.0 """ from sage.modules.free_module_element import vector from sage.numerical.optimize import minimize_constrained def func(z): vz = vector(z) return - (M*vz).norm(p) / vz.norm(p) cons = [lambda z: v * vector(z), lambda z: - v * vector(z)] x0 = range(len(v)) x0[0] = v[1] x0[1] = -v[0] rep = minimize_constrained(func, cons, x0) if verbose: print(rep, rep.norm(), rep*v) return -func(rep)
def relative_field_representation(self, b): r""" Returns a vector representation of the field element ``b`` in the basis of the absolute field over the relative field. INPUT: - ``b`` -- an element of the absolute field EXAMPLES:: sage: from sage.coding.relative_finite_field_extension import * sage: Fqm.<aa> = GF(16) sage: Fq.<a> = GF(4) sage: FE = RelativeFiniteFieldExtension(Fqm, Fq) sage: b = aa^3 + aa^2 + aa + 1 sage: FE.relative_field_representation(b) (1, a + 1) """ if not b in self.absolute_field(): raise ValueError("The input has to be an element of the absolute field") s = self.relative_field_degree() if s == 1: return vector(b) else: Fq = self.relative_field() vect = self._flattened_relative_field_representation(b) sm = self.absolute_field_degree() list_elts = [] for i in range(0, sm, s): list_elts.append(Fq(vect[i:i+s])) return vector(Fq, list_elts)
def __init__(self, v, omega, mu=0, prec=None): r""" EXAMPLES:: sage: from slabbe import DiscreteHyperplane sage: p = DiscreteHyperplane([1,pi,7], 1+pi+7, mu=0) sage: p Set of points x in ZZ^3 satisfying: 0 <= (1, pi, 7) . x + 0 < pi + 8 :: sage: p = DiscreteHyperplane([1,pi,7], 1+pi+7, mu=20) sage: vector((0,0,0)) in p False sage: p = DiscreteHyperplane([1,pi,7], 1+pi+7, mu=0) sage: vector((0,0,0)) in p True """ if prec is None: self._v = vector(v) self._omega = omega self._mu = mu else: RF = RealField(prec=prec) self._v = vector(RF, v) self._omega = RF(omega) self._mu = RF(mu) def contain(p): #print("est-ce proche : ", self._v.dot_product(p) + self._mu) return 0 <= self._v.dot_product(p) + self._mu < self._omega DiscreteSubset.__init__(self, dimension=len(self._v), predicate=contain)
def parallelotope(self, generators): r""" Return the parallelotope spanned by the generators. INPUT: - ``generators`` -- an iterable of anything convertible to vector (for example, a list of vectors) such that the vectors all have the same dimension. OUTPUT: The parallelotope. This is the multi-dimensional generalization of a parallelogram (2 generators) and a parallelepiped (3 generators). EXAMPLES:: sage: polytopes.parallelotope([ (1,0), (0,1) ]) A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices sage: polytopes.parallelotope([[1,2,3,4],[0,1,0,7],[3,1,0,2],[0,0,1,0]]) A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 16 vertices """ try: generators = [ vector(QQ,v) for v in generators ] base_ring = QQ except TypeError: generators = [ vector(RDF,v) for v in generators ] base_ring = RDF from sage.combinat.combination import Combinations par = [ 0*generators[0] ] par += [ sum(c) for c in Combinations(generators) if c!=[] ] return Polyhedron(vertices=par, base_ring=base_ring)
def first_coordinate_plane(self): """ Restrict to the first coordinate plane. OUTPUT: A new double description pair with the constraint `x_0 = 0` added. EXAMPLES:: sage: A = matrix([(1, 1), (-1, 1)]) sage: from sage.geometry.polyhedron.double_description import StandardAlgorithm sage: DD, _ = StandardAlgorithm(A).initial_pair() sage: DD Double description pair (A, R) defined by A = [ 1 1], R = [ 1/2 -1/2] [-1 1] [ 1/2 1/2] sage: DD.first_coordinate_plane() Double description pair (A, R) defined by [ 1 1] A = [-1 1], R = [ 0] [-1 0] [1/2] [ 1 0] """ R = self.problem.base_ring() d = self.problem.dim() a_neg = vector(R, [-self.one] + [self.zero] * (d - 1)) a_pos = vector(R, [+self.one] + [self.zero] * (d - 1)) new = self._make_new(self.A, self.R) new.add_inequality(a_neg) new.add_inequality(a_pos) return new
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 plot_fan_stereographically(rays, walls, northsign=1, north=vector((-1,-1,-1)), right=vector((1,0,0)), colors=None, thickness=None): from sage.plot.graphics import Graphics from sage.plot.point import point from sage.misc.flatten import flatten from sage.plot.line import line from sage.misc.functional import norm if colors == None: colors = dict([('walls','black'),('rays','red')]) if thickness == None: thickness = dict([('walls',0.5),('rays',20)]) G = Graphics() for (u,v) in walls: G += _stereo_arc(vector(u),vector(v),vector(u+v),north=northsign*north,right=right,color=colors['walls'],thickness=thickness['walls'],zorder=len(G)) for v in rays: G += point(_stereo_coordinates(vector(v),north=northsign*north,right=right),color=colors['rays'],zorder=len(G),size=thickness['rays']) G.set_aspect_ratio(1) G._show_axes = False return G
def decode_to_code(self, y): r""" Corrects the errors in ``word`` and returns a codeword. EXAMPLES:: sage: C = codes.GeneralizedReedSolomonCode(GF(16, 'aa').list()[:13], 5) sage: Cs = codes.SubfieldSubcode(C, GF(4, 'a')) sage: D = codes.decoders.SubfieldSubcodeOriginalCodeDecoder(Cs) sage: Chan = channels.StaticErrorRateChannel(Cs.ambient_space(), D.decoding_radius()) sage: c = Cs.random_element() sage: y = Chan(c) sage: c == D.decode_to_code(y) True """ C = self.code() D = self.original_decoder() FE = C.embedding() phi = FE.embedding() y_or = vector([phi(i) for i in y]) c_or = D.decode_to_code(y_or) if 'list-decoder' in self.decoder_type(): result = [] for c in c_or: if all(FE.is_in_relative_field(x) for x in c): result.append(vector(map(FE.cast_into_relative_field, c))) return result else: if all(FE.is_in_relative_field(x) for x in c_or): return vector([FE.cast_into_relative_field(i, check=False) for i in c_or]) else: raise DecodingError("Original decoder does not output a " "subfield codeword. You may have exceeded the decoding radius.")
def __init__(self, v, start=(0,0,0)): r""" EXAMPLES:: sage: from slabbe import BilliardCube sage: b = BilliardCube((1,pi,sqrt(2))) sage: b Cubic billiard of direction (1, pi, sqrt(2)) TESTS:: sage: vector((0,0,0)) in b True sage: vector((0,0,1)) in b False sage: vector((0,1,0)) in b True sage: vector((1,0,0)) in b False sage: vector((0,-1,0)) in b True """ a,b,c = self._v = vector(v) sx,sy,sz = self._start = vector(start) px = DiscretePlane([0,c,-b], b+c, mu=(b+c)/2 - sy*c + sz*b) py = DiscretePlane([c,0,-a], a+c, mu=(a+c)/2 - sx*c + sz*a) pz = DiscretePlane([b,-a,0], a+b, mu=(a+b)/2 - sx*b + sy*a) Intersection.__init__(self, (px,py,pz))
def platonic_dodecahedron(): r"""Produce a triple consisting of a polyhedral version of the platonic dodecahedron, the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface to the polyhedron. EXAMPLES:: sage: from flatsurf.geometry.polyhedra import platonic_dodecahedron sage: polyhedron,surface,surface_to_polyhedron = platonic_dodecahedron() sage: TestSuite(surface).run() r""" vertices=[] phi=AA(1+sqrt(5))/2 F=NumberField(phi.minpoly(),"phi",embedding=phi) phi=F.gen() for x in xrange(-1,3,2): for y in xrange(-1,3,2): for z in xrange(-1,3,2): vertices.append(vector(F,(x,y,z))) for x in xrange(-1,3,2): for y in xrange(-1,3,2): vertices.append(vector(F,(0,x*phi,y/phi))) vertices.append(vector(F,(y/phi,0,x*phi))) vertices.append(vector(F,(x*phi,y/phi,0))) scale=AA(2/sqrt(1+(phi-1)**2+(1/phi-1)**2)) p=Polyhedron(vertices=vertices) s,m = polyhedron_to_cone_surface(p,scaling_factor=scale) return p,s,m
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 __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 _arc(p,q,s,**kwds): #rewrite this to use polar_plot and get points to do filled triangles from sage.misc.functional import det from sage.plot.line import line from sage.misc.functional import norm from sage.symbolic.all import pi from sage.plot.arc import arc p,q,s = map( lambda x: vector(x), [p,q,s]) # to avoid running into division by 0 we set to be colinear vectors that are # almost colinear if abs(det(matrix([p-s,q-s])))<0.01: return line((p,q),**kwds) (cx,cy)=var('cx','cy') equations=[ 2*cx*(s[0]-p[0])+2*cy*(s[1]-p[1]) == s[0]**2+s[1]**2-p[0]**2-p[1]**2, 2*cx*(s[0]-q[0])+2*cy*(s[1]-q[1]) == s[0]**2+s[1]**2-q[0]**2-q[1]**2 ] c = vector( [solve( equations, (cx,cy), solution_dict=True )[0][i] for i in [cx,cy]] ) r = norm(p-c) a_p,a_q,a_s = map( _to_angle, [p-c,q-c,s-c]) angles = [a_p,a_q,a_s] angles.sort() if a_s == angles[0]: return arc( c, r, angle=angles[2], sector=(0,2*pi-angles[2]+angles[1]), **kwds) if a_s == angles[1]: return arc( c, r, angle=angles[0], sector=(0,angles[2]-angles[0]), **kwds) if a_s == angles[2]: return arc( c, r, angle=angles[1], sector=(0,2*pi-angles[1]+angles[0]), **kwds)
def space(self): r''' Calculates the homology space as a Z-module. ''' verb = get_verbose() set_verbose(0) V = self.coefficient_module() R = V.base_ring() Vdim = V.dimension() G = self.group() gens = G.gens() ambient = R**(Vdim * len(gens)) if self.trivial_action(): cycles = ambient else: # Now find the subspace of cycles A = Matrix(R, Vdim, 0) for g in gens: for v in V.gens(): A = A.augment(matrix(R,Vdim,1,list(vector(g**-1 * v - v)))) K = A.right_kernel_matrix() cycles = ambient.submodule([ambient(list(o)) for o in K.rows()]) boundaries = [] for r in G.get_relation_words(): grad = self.twisted_fox_gradient(G(r).word_rep) for v in V.gens(): boundaries.append(cycles(ambient(sum([list(a * vector(v)) for a in grad],[])))) boundaries = cycles.submodule(boundaries) ans = cycles.quotient(boundaries) set_verbose(verb) return ans
def plot_cluster_fan_stereographically(self, northsign=1, north=None, right=None, colors=None): from sage.plot.graphics import Graphics from sage.plot.point import point from sage.misc.flatten import flatten from sage.plot.line import line from sage.misc.functional import norm if self.rk !=3: raise ValueError("Can only stereographically project fans in 3d.") if not self.is_finite() and self._depth == infinity: raise ValueError("For infinite algebras you must specify the depth.") if north == None: if self.is_affine(): north = vector(self.delta()) else: north = vector( (-1,-1,-1) ) if right == None: if self.is_affine(): right = vector(self.gamma()) else: right = vector( (1,0,0) ) if colors == None: colors = dict([(0,'red'),(1,'green'),(2,'blue'),(3,'cyan'),(4,'yellow')]) G = Graphics() roots = list(self.g_vectors()) compatible = [] while roots: x = roots.pop() for y in roots: if self.compatibility_degree(x,y) == 0: compatible.append((x,y)) for (u,v) in compatible: G += _stereo_arc(vector(u),vector(v),vector(u+v),north=northsign*north,right=right,thickness=0.5,color='black') for i in range(3): orbit = self.ith_orbit(i) for j in orbit: G += point(_stereo_coordinates(vector(orbit[j]),north=northsign*north,right=right),color=colors[i],zorder=len(G)) if self.is_affine(): tube_vectors = map(vector,flatten(self.affine_tubes())) for v in tube_vectors: G += point(_stereo_coordinates(v,north=northsign*north,right=right),color=colors[3],zorder=len(G)) if north != vector(self.delta()): G += _stereo_arc(tube_vectors[0],tube_vectors[1],vector(self.delta()),north=northsign*north,right=right,thickness=2,color=colors[4],zorder=0) else: # FIXME: refactor this before publishing tube_projections = [ _stereo_coordinates(v,north=northsign*north,right=right) for v in tube_vectors ] t=min((G.get_minmax_data()['xmax'],G.get_minmax_data()['ymax'])) G += line([tube_projections[0],tube_projections[0]+t*(_normalize(tube_projections[0]-tube_projections[1]))],thickness=2,color=colors[4],zorder=0) G += line([tube_projections[1],tube_projections[1]+t*(_normalize(tube_projections[1]-tube_projections[0]))],thickness=2,color=colors[4],zorder=0) G.set_aspect_ratio(1) G._show_axes = False return G
def closest_vector(self, t): """ Compute the closest vector in the embedded lattice to a given vector. INPUT: - ``t`` -- the target vector to compute the closest vector to OUTPUT: The vector in the lattice closest to ``t``. EXAMPLES:: sage: from sage.modules.free_module_integer import IntegerLattice sage: L = IntegerLattice([[1, 0], [0, 1]]) sage: L.closest_vector((-6, 5/3)) (-6, 2) ALGORITHM: Uses the algorithm from [Mic2010]_. REFERENCES: .. [Mic2010] D. Micciancio, P. Voulgaris. *A Deterministic Single Exponential Time Algorithm for Most Lattice Problems based on Voronoi Cell Computations*. Proceedings of the 42nd ACM Symposium Theory of Computation, 2010. """ voronoi_cell = self.voronoi_cell() def projection(M, v): Mt = M.transpose() P = Mt * (M * Mt) ** (-1) * M return P * v t = projection(matrix(self.reduced_basis), vector(t)) def CVPP_2V(t, V, voronoi_cell): t_new = t while not voronoi_cell.contains(t_new.list()): v = max(V, key=lambda v: t_new * v / v.norm() ** 2) t_new = t_new - v return t - t_new V = self.voronoi_relevant_vectors() t = vector(t) p = 0 while not (ZZ(2 ** p) * voronoi_cell).contains(t): p += 1 t_new = t i = p while i >= 1: V_scaled = [v * (2 ** (i - 1)) for v in V] t_new = t_new - CVPP_2V(t_new, V_scaled, ZZ(2 ** (i - 1)) * voronoi_cell) i -= 1 return t - t_new
def __init__(self, A, elt=None, check=True): """ TESTS:: sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1,0], [0,1]]), Matrix([[0,1], [0,0]])]) sage: A(QQ(4)) Traceback (most recent call last): ... TypeError: elt should be a vector, a matrix, or an element of the base field sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1], [-1,0]])]) sage: elt = B(Matrix([[1,1], [-1,1]])); elt e0 + e1 sage: TestSuite(elt).run() sage: B(Matrix([[0,1], [1,0]])) Traceback (most recent call last): ... ValueError: matrix does not define an element of the algebra """ AlgebraElement.__init__(self, A) k = A.base_ring() n = A.degree() if elt is None: self._vector = vector(k, n) self._matrix = Matrix(k, n) else: if isinstance(elt, int): elt = Integer(elt) elif isinstance(elt, list): elt = vector(elt) if A == elt.parent(): self._vector = elt._vector.base_extend(k) self._matrix = elt._matrix.base_extend(k) elif k.has_coerce_map_from(elt.parent()): e = k(elt) if e == 0: self._vector = vector(k, n) self._matrix = Matrix(k, n) elif A.is_unitary(): self._vector = A._one * e self._matrix = Matrix.identity(k, n) * e else: raise TypeError("algebra is not unitary") elif is_Vector(elt): self._vector = elt.base_extend(k) self._matrix = Matrix(k, sum([elt[i] * A.table()[i] for i in xrange(n)])) elif is_Matrix(elt): if not A.is_unitary(): raise TypeError("algebra is not unitary") self._vector = A._one * elt if not check or sum([self._vector[i]*A.table()[i] for i in xrange(n)]) == elt: self._matrix = elt else: raise ValueError("matrix does not define an element of the algebra") else: raise TypeError("elt should be a vector, a matrix, " + "or an element of the base field")
def has_rational_point(self, point = False, algorithm = 'default', read_cache = True): r""" Returns True if and only if the conic ``self`` has a point over its base field `B`. If ``point`` is True, then returns a second output, which is a rational point if one exists. Points are cached whenever they are found. Cached information is used if and only if ``read_cache`` is True. EXAMPLES: sage: Conic(RR, [1, 1, 1]).has_rational_point() False sage: Conic(CC, [1, 1, 1]).has_rational_point() True sage: Conic(RR, [1, 2, -3]).has_rational_point(point = True) (True, (1.73205080756888 : 0.000000000000000 : 1.00000000000000)) """ if read_cache: if self._rational_point is not None: if point: return True, self._rational_point else: return True B = self.base_ring() if is_ComplexField(B): if point: [_,_,_,d,e,f] = self._coefficients if d == 0: return True, self.point([0,1,0]) return True, self.point([0, ((e**2-4*d*f).sqrt()-e)/(2*d), 1], check = False) return True if is_RealField(B): D, T = self.diagonal_matrix() [a, b, c] = [D[0,0], D[1,1], D[2,2]] if a == 0: ret = True, self.point(T*vector([1,0,0]), check = False) elif a*c <= 0: ret = True, self.point(T*vector([(-c/a).sqrt(),0,1]), check = False) elif b == 0: ret = True, self.point(T*vector([0,1,0]), check = False) elif b*c <= 0: ret = True, self.point(T*vector([0,(-c/b).sqrt(),0,1]), check = False) else: ret = False, None if point: return ret return ret[0] raise NotImplementedError, "has_rational_point not implemented for " \ "conics over base field %s" % B
def _normalize(x): r""" make x of length 1 """ from sage.misc.functional import norm x=vector(RR, x) if norm(x) == 0: return x return vector(x/norm(x))
def _stereo_arc(x,y, xy=None, north=(1,0,0), right=(0,1,0), translation=-1, **kwds): from sage.misc.functional import n x=vector(x) y=vector(y) sx=n(_stereo_coordinates(x, north=north, right=right, translation=translation)) sy=n(_stereo_coordinates(y, north=north, right=right, translation=translation)) if xy == None: xy=x+y sxy=n(_stereo_coordinates(xy, north=north, right=right, translation=translation)) return _arc(sx,sy,sxy,**kwds)
def decode_to_code(self, y, **kwargs): r""" Decodes ``y`` to an element in :meth:`sage.coding.decoder.Decoder.code`. EXAMPLES:: sage: C = codes.GeneralizedReedSolomonCode(GF(16, 'a').list()[:15], 7) sage: Ce = codes.ExtendedCode(C) sage: D = codes.decoders.ExtendedCodeOriginalCodeDecoder(Ce) sage: c = Ce.random_element() sage: Chan = channels.StaticErrorRateChannel(Ce.ambient_space(), D.decoding_radius()) sage: y = Chan(c) sage: y in Ce False sage: D.decode_to_code(y) == c True Another example, with a list decoder:: sage: C = codes.GeneralizedReedSolomonCode(GF(16, 'a').list()[:15], 7) sage: Ce = codes.ExtendedCode(C) sage: Dgrs = C.decoder('GuruswamiSudan', tau = 4) sage: D = codes.decoders.ExtendedCodeOriginalCodeDecoder(Ce, original_decoder = Dgrs) sage: c = Ce.random_element() sage: Chan = channels.StaticErrorRateChannel(Ce.ambient_space(), D.decoding_radius()) sage: y = Chan(c) sage: y in Ce False sage: c in D.decode_to_code(y) True """ D = self.original_decoder() C = self.code() F = C.base_field() n = C.length() y_original = copy(y.list()) y_original.pop(n - 1) decoded = D.decode_to_code(vector(y_original), **kwargs) if 'list-decoder' in self.decoder_type(): l = [] for word in decoded: last_pos = F.zero() for i in word: last_pos += i word_list = list(word) word_list.append(last_pos) l.append(vector(F, word_list)) return l else: last_pos = F.zero() for i in decoded: last_pos += i decoded_list = list(decoded) decoded_list.append(last_pos) return vector(F, decoded_list)
def encode(self, m, S): ''' encodes a vector m (in Zmod(q)^n) to index set S ''' zinv = prod([self.zinv[i] for i in S]) m = vector(Zmod(self.q),m) zero = vector(Zmod(self.q),self.D_sigmap_I()) # random encoding of 0 c = self.Rq(list(zero + m)) return c * zinv
def __call__(self,w, field = None): r""" Return the image of ``w`` under the similarity. Here ``w`` may be a ConvexPolygon or a vector (or something that can be indexed in the same way as a vector). If a field is provided, the objects returned will be defined over this field. TESTS:: sage: from flatsurf.geometry.similarity import SimilarityGroup sage: S = SimilarityGroup(AA) sage: a = S((1,-1,AA(2).sqrt(),0)) sage: a((1,2)) (4.414213562373095?, 1) sage: a.matrix()*vector((1,2,1)) (4.414213562373095?, 1, 1) sage: from flatsurf.geometry.similarity import SimilarityGroup sage: SG = SimilarityGroup(QQ) sage: from flatsurf.geometry.polygon import Polygons sage: P = Polygons(QQ) sage: p = P.an_element() sage: p Polygon: (0, 0), (1, 0), (1, 1), (0, 1) sage: g = SG.an_element()**2 sage: g (x, y) |-> (25*x + 4, 25*y + 10) sage: g(p) Polygon: (4, 10), (29, 10), (29, 35), (4, 35) sage: g(p, field=AA).parent() polygons with coordinates in Algebraic Real Field """ if isinstance(w,ConvexPolygon): if field is None: P = Polygons(field=self.parent().base_field()) else: P = Polygons(field=field) try: return P(vertices=[self(v) for v in w.vertices()]) except ValueError as e: if not self._sign.is_one(): raise ValueError("Similarity must be orientation preserving.") else: # Not sure why this would happen: raise e if field is None: if self._sign.is_one(): return vector([self._a*w[0]-self._b*w[1]+self._s, self._b*w[0]+self._a*w[1]+self._t]) else: return vector([self._a*w[0]+self._b*w[1]+self._s, self._b*w[0]-self._a*w[1]+self._t]) else: if self._sign.is_one(): return vector(field, [self._a*w[0]-self._b*w[1]+self._s, self._b*w[0]+self._a*w[1]+self._t]) else: return vector(field, [self._a*w[0]+self._b*w[1]+self._s, self._b*w[0]-self._a*w[1]+self._t])
def repr_pretty(coefficients, type, prefix='x', indices=None, latex=False): r""" Return a pretty representation of equation/inequality represented by the coefficients. INPUT: - ``coefficients`` -- a tuple or other iterable - ``type`` -- either ``0`` (``PolyhedronRepresentation.INEQUALITY``) or ``1`` (``PolyhedronRepresentation.EQUATION``) - ``prefix`` -- a string - ``indices`` -- a tuple or other iterable - ``latex`` -- a boolean OUTPUT: A string. EXAMPLES:: sage: from sage.geometry.polyhedron.representation import repr_pretty sage: from sage.geometry.polyhedron.representation import PolyhedronRepresentation sage: print(repr_pretty((0, 1, 0, 0), PolyhedronRepresentation.INEQUALITY)) x0 >= 0 sage: print(repr_pretty((1, 2, 1, 0), PolyhedronRepresentation.INEQUALITY)) 2*x0 + x1 + 1 >= 0 sage: print(repr_pretty((1, -1, -1, 1), PolyhedronRepresentation.EQUATION)) x2 + 1 == x0 + x1 """ from sage.misc.latex import latex as latex_function from sage.modules.free_module_element import vector from sage.symbolic.ring import SR coeffs = vector(coefficients) if indices is None: indices = range(len(coeffs)-1) vars = vector([1] + list(SR(prefix + '{}'.format(i)) for i in indices)) positive_part = vector([max(c, 0) for c in coeffs]) negative_part = - (coeffs - positive_part) assert coeffs == positive_part - negative_part if type == PolyhedronRepresentation.EQUATION: rel = '=' if latex else '==' elif type == PolyhedronRepresentation.INEQUALITY: rel = r'\geq' if latex else '>=' else: raise NotImplementedError( 'no pretty printing available: wrong type {}'.format(type)) f = latex_function if latex else repr return '{} {} {}'.format(f(positive_part*vars), rel, f(negative_part*vars))
def d_vector(self): n = self.parent().rk one = self.parent().ambient_field()(1) factors = self.lift_to_field().factor() initial = [] non_initial = [] [(initial if x[1] > 0 and len(x[0].monomials()) == 1 else non_initial).append(x[0]**x[1]) for x in factors] initial = prod(initial+[one]).numerator() non_initial = prod(non_initial+[one]).denominator() v1 = vector(non_initial.exponents()[0][:n]) v2 = vector(initial.exponents()[0][:n]) return tuple(v1-v2)
def __init__(self, params, asym=False): (self.n, self.q, sigma, self.sigma_prime, self.k) = params S, x = PolynomialRing(ZZ, 'x').objgen() self.R = S.quotient_ring(S.ideal(x**self.n + 1)) Sq = PolynomialRing(Zmod(self.q), 'x') self.Rq = Sq.quotient_ring(Sq.ideal(x**self.n + 1)) # draw z_is uniformly from Rq and compute its inverse in Rq if asym: z = [self.Rq.random_element() for i in range(self.k)] self.zinv = [z_i**(-1) for z_i in z] else: # or do symmetric version z = self.Rq.random_element() zinv = z**(-1) z, self.zinv = zip(*[(z,zinv) for i in range(self.k)]) # set up some discrete Gaussians DGSL_sigma = DGSL(ZZ**self.n, sigma) self.D_sigma = lambda: self.Rq(list(DGSL_sigma())) # discrete Gaussian in ZZ^n with stddev sigma_prime, yields random level-0 encodings DGSL_sigmap_ZZ = DGSL(ZZ**self.n, self.sigma_prime) self.D_sigmap_ZZ = lambda: self.Rq(list(DGSL_sigmap_ZZ())) # draw g repeatedly from a Gaussian distribution of Z^n (with param sigma) # until g^(-1) in QQ[x]/<x^n + 1> is small (< n^2) Sk = PolynomialRing(QQ, 'x') K = Sk.quotient_ring(Sk.ideal(x**self.n + 1)) while True: l = self.D_sigma() ginv_K = K(mod_near_poly(l, self.q))**(-1) ginv_size = vector(ginv_K).norm() if ginv_size < self.n**2: g = self.Rq(l) self.ginv = g**(-1) break # discrete Gaussian in I = <g>, yields random encodings of 0 short_g = vector(ZZ, mod_near_poly(g,self.q)) DGSL_sigmap_I = DGSL(short_g, self.sigma_prime) self.D_sigmap_I = lambda: self.Rq(list(DGSL_sigmap_I())) # compute zero-testing parameter p_zt # randomly draw h (in Rq) from a discrete Gaussian with param q^(1/2) self.h = self.Rq(list(DGSL(ZZ**self.n, round(sqrt(self.q)))())) # create p_zt self.p_zt = self.ginv * self.h * prod(z)
def closest_vector(self, t): """ Compute the closest vector in the embedded lattice to a given vector. INPUT: - ``t`` -- the target vector to compute the closest vector to OUTPUT: The vector in the lattice closest to ``t``. EXAMPLES:: sage: from sage.modules.free_module_integer import IntegerLattice sage: L = IntegerLattice([[1, 0], [0, 1]]) sage: L.closest_vector((-6, 5/3)) (-6, 2) ALGORITHM: Uses the algorithm from [MV2010]_. """ voronoi_cell = self.voronoi_cell() def projection(M, v): Mt = M.transpose() P = Mt * (M * Mt) ** (-1) * M return P * v t = projection(matrix(self.reduced_basis), vector(t)) def CVPP_2V(t, V, voronoi_cell): t_new = t while not voronoi_cell.contains(t_new.list()): v = max(V, key=lambda v: t_new * v / v.norm() ** 2) t_new = t_new - v return t - t_new V = self.voronoi_relevant_vectors() t = vector(t) p = 0 while not (ZZ(2 ** p) * voronoi_cell).contains(t): p += 1 t_new = t i = p while i >= 1: V_scaled = [v * (2 ** (i - 1)) for v in V] t_new = t_new - CVPP_2V(t_new, V_scaled, ZZ(2 ** (i - 1)) * voronoi_cell) i -= 1 return t - t_new
def _stereo_coordinates(x, north=(1,0,0), right=(0,1,0), translation=-1): r""" Project stereographically points from a sphere """ from sage.misc.functional import norm north=_normalize(north) right=vector(right) right=_normalize(right-(right*north)*north) if norm(right) == 0: raise ValueError ("Right must not be linearly dependent from north") top=north.cross_product(right) x=_normalize(x) p=(translation-north*x)/(1-north*x)*(north-x)+x return vector((right*p, top*p ))
def tangent_vector(self, lab, p, v, ring=None): r""" Return a tangent vector. INPUT: - ``lab`` -- label of a polygon - ``p`` -- coordinates of a point in the polygon - ``v`` -- coordinates of a vector in R^2 EXAMPLES:: sage: from flatsurf.geometry.chamanara import chamanara_surface sage: S = chamanara_surface(1/2) sage: S.tangent_vector(S.base_label(), (1/2,1/2), (1,1)) SimilaritySurfaceTangentVector in polygon (1, [-1 0] [ 0 -1]) based at (1/2, -3/2) with vector (1, 1) sage: K.<sqrt2> = QuadraticField(2) sage: S.tangent_vector(S.base_label(), (1/2,1/2), (1,sqrt2)) SimilaritySurfaceTangentVector in polygon (1, [-1 0] [ 0 -1]) based at (1/2, -3/2) with vector (1, sqrt2) """ p = vector(p) v = vector(v) if p.parent().dimension() != 2 or v.parent().dimension() != 2: raise ValueError("p (={!r}) and v (={!v}) should have two coordinates") if ring is None: R = p.base_ring() if R != v.base_ring(): from sage.structure.element import get_coercion_model cm = get_coercion_model() R = cm.common_parent(R, v.base_ring()) p = p.change_ring(R) v = v.change_ring(R) R2 = self.base_ring() if R != R2: if R2.has_coerce_map_from(R): p = p.change_ring(R2) v = v.change_ring(R2) R = R2 elif not R.has_coerce_map_from(R2): raise ValueError("not able to find a common ring for arguments") return self.tangent_bundle(R)(lab, p, v) else: return self.tangent_bundle(ring)(lab, p, v)
def __init__(self, n, q, D, secret_dist='uniform', m=None): """ Construct an LWE oracle in dimension ``n`` over a ring of order ``q`` with noise distribution ``D``. INPUT: - ``n`` - dimension (integer > 0) - ``q`` - modulus typically > n (integer > 0) - ``D`` - an error distribution such as an instance of :class:`DiscreteGaussianSamplerRejection` or :class:`UniformSampler` - ``secret_dist`` - distribution of the secret; one of - "uniform" - secret follows the uniform distribution in `\Zmod{q}` - "noise" - secret follows the noise distrbution - ``(lb,ub)`` - the secret is chosen uniformly from ``[lb,...,ub]`` including both endpoints - ``m`` - number of allowed samples or ``None`` if no such limit exists (default: ``None``) EXAMPLE: First, we construct a noise distribution with standard deviation 3.0:: sage: D = DiscreteGaussianSamplerRejection(3.0) Next, we construct our oracle:: sage: lwe = LWE(n=20, q=next_prime(400), D=D); lwe LWE(20, 401, DiscreteGaussianSamplerRejection(3.000000, 53, 4), 'uniform', None) and sample 1000 samples:: sage: L = [lwe() for _ in range(1000)] To test the oracle, we use the internal secret to evaluate the samples in the secret:: sage: S = [ZZ(a.dot_product(lwe._LWE__s) - c) for (a,c) in L] However, while Sage represents finite field elements between 0 and q-1 we rely on a balanced representation of those elements here. Hence, we fix the representation and recover the correct standard deviation of the noise:: sage: sqrt(variance([e if e <= 200 else e-401 for e in S]).n()) 3.0... If ``m`` is not ``None`` the number of available samples is restricted:: sage: lwe = LWE(n=20, q=next_prime(400), D=D, m=30) sage: _ = [lwe() for _ in range(30)] sage: lwe() # 31 Traceback (most recent call last): ... IndexError: Number of available samples exhausted. """ self.n = ZZ(n) self.m = m self.__i = 0 self.K = IntegerModRing(q) self.FM = FreeModule(self.K, n) self.D = D self.secret_dist = secret_dist if secret_dist == 'uniform': self.__s = random_vector(self.K, self.n) elif secret_dist == 'noise': self.__s = vector(self.K, self.n, [self.D() for _ in range(n)]) else: try: lb, ub = map(ZZ, secret_dist) self.__s = vector(self.K, self.n, [randint(lb, ub) for _ in range(n)]) except (IndexError, TypeError): raise TypeError("Parameter secret_dist=%s not understood." % (secret_dist))
def func(z): vz = vector(z) return - (M*vz).norm(p) / vz.norm(p)
def solve(self, c=0): r""" Return a vector `x` such that ``self(x) == c``. INPUT: - ``c`` -- (default: 0) a rational number. OUTPUT: - A non-zero vector `x` satisfying ``self(x) == c``. ALGORITHM: Uses PARI's qfsolve(). Algorithm described by Jeroen Demeyer; see comments on :trac:`19112` EXAMPLES:: sage: F = DiagonalQuadraticForm(QQ, [1, -1]); F Quadratic form in 2 variables over Rational Field with coefficients: [ 1 0 ] [ * -1 ] sage: F.solve() (1, 1) sage: F.solve(1) (1, 0) sage: F.solve(2) (3/2, -1/2) sage: F.solve(3) (2, -1) :: sage: F = DiagonalQuadraticForm(QQ, [1, 1, 1, 1]) sage: F.solve(7) (1, 2, -1, -1) sage: F.solve() Traceback (most recent call last): ... ArithmeticError: no solution found (local obstruction at -1) :: sage: Q = QuadraticForm(QQ, 2, [17, 94, 130]) sage: x = Q.solve(5); x (17, -6) sage: Q(x) 5 sage: Q.solve(6) Traceback (most recent call last): ... ArithmeticError: no solution found (local obstruction at 3) sage: G = DiagonalQuadraticForm(QQ, [5, -3, -2]) sage: x = G.solve(10); x (3/2, -1/2, 1/2) sage: G(x) 10 sage: F = DiagonalQuadraticForm(QQ, [1, -4]) sage: x = F.solve(); x (2, 1) sage: F(x) 0 :: sage: F = QuadraticForm(QQ, 4, [0, 0, 1, 0, 0, 0, 1, 0, 0, 0]); F Quadratic form in 4 variables over Rational Field with coefficients: [ 0 0 1 0 ] [ * 0 0 1 ] [ * * 0 0 ] [ * * * 0 ] sage: F.solve(23) (23, 0, 1, 0) Other fields besides the rationals are currently not supported:: sage: F = DiagonalQuadraticForm(GF(11), [1, 1]) sage: F.solve() Traceback (most recent call last): ... TypeError: solving quadratic forms is only implemented over QQ """ if self.base_ring() is not QQ: raise TypeError("solving quadratic forms is only implemented over QQ") M = self.Gram_matrix() # If no argument passed for c, we just pass self into qfsolve(). if not c: x = qfsolve(M) if isinstance(x, Integer): raise ArithmeticError( "no solution found (local obstruction at {})".format(x)) return x # If c != 0, define a new quadratic form Q = self - c*z^2 d = self.dim() N = Matrix(self.base_ring(), d + 1, d + 1) for i in range(d): for j in range(d): N[i, j] = M[i, j] N[d, d] = -c # Find a solution x to Q(x) = 0, using qfsolve() x = qfsolve(N) # Raise an error if qfsolve() doesn't find a solution if isinstance(x, Integer): raise ArithmeticError( "no solution found (local obstruction at {})".format(x)) # Let z be the last term of x, and remove z from x z = x[-1] x = x[:-1] # If z != 0, then Q(x/z) = c if z: return x * (1 / z) # Case 2: We found a solution self(x) = 0. Let e be any vector such # that B(x,e) != 0, where B is the bilinear form corresponding to self. # To find e, just try all unit vectors (0,..0,1,0...0). # Let a = (c - self(e))/(2B(x,e)) and let y = e + a*x. # Then self(y) = B(e + a*x, e + a*x) = self(e) + 2B(e, a*x) # = self(e) + 2([c - self(e)]/[2B(x,e)]) * B(x,e) = c. e = vector([1] + [0] * (d - 1)) i = 0 while self.bilinear_map(x, e) == 0: e[i] = 0 i += 1 e[i] = 1 a = (c - self(e)) / (2 * self.bilinear_map(x, e)) return e + a * x
def series_sum_ordinary(Intervals, dop, bwrec, ini, pt, stop, stride): jet = pt.jet(Intervals) Jets = jet.parent() # polynomial ring! ord = pt.jet_order jetpow = Jets.one() radpow = bounds.IR.one() ordrec = bwrec.order assert ini.expo.is_zero() last = collections.deque([Intervals.zero()] * (ordrec - dop.order() + 1)) last.extend( Intervals(ini.shift[n][0]) for n in xrange(dop.order() - 1, -1, -1)) assert len(last) == ordrec + 1 # not ordrec! psum = Jets.zero() tail_bound = bounds.IR(infinity) start = dop.order() # Evaluate the coefficients a bit in advance as we are going to need them to # compute the residuals. This is not ideal at high working precision, but # already saves a lot of time compared to doing the evaluations twice. bwrec_ev = bwrec.eval_method(Intervals) bwrec_nplus = collections.deque( (bwrec_ev(start + i) for i in xrange(ordrec)), maxlen=ordrec) def get_bound(maj): return maj.bound(pt.rad, rows=ord) def get_residuals(): return [ stop.maj.normalized_residual(n, [[c] for c in last][1:], [[[c] for c in l] for l in bwrec_nplus]) ] def get_value(): return psum for n in itertools.count(): last.rotate(1) #last[0] = None # At this point last[0] should be considered undefined (it will hold # the coefficient of z^n later in the loop body) and last[1], ... # last[ordrec] are the coefficients of z^(n-1), ..., z^(n-ordrec) if n % stride == 0: radpowest = abs( jetpow[0] if pt.is_numeric else Intervals(pt.rad**n)) est = sum(abs(a) for a in last) * radpowest done, tail_bound = stop.check(get_bound, get_residuals, get_value, (n <= start), n, tail_bound, est, stride) if done: break if n >= start: bwrec_n = (bwrec_nplus[0] if bwrec_nplus else bwrec_ev(n)) comb = sum(bwrec_n[k] * last[k] for k in xrange(1, ordrec + 1)) last[0] = -~bwrec_n[0] * comb bwrec_nplus.append(bwrec_ev(n + bwrec.order)) # logger.debug("n = %s, [c(n), c(n-1), ...] = %s", n, list(last)) term = Jets(last[0])._mul_trunc_(jetpow, ord) psum += term jetpow = jetpow._mul_trunc_(jet, ord) radpow *= pt.rad logger.info( "summed %d terms, tail <= %s (est = %s), coeffwise error <= %s", n, tail_bound, bounds.IR(est), max(psum[i].rad() for i in range(ord)) if pt.is_numeric else "n/a") # Account for the dropped high-order terms in the intervals we return # (tail_bound is actually a bound on the Frobenius norm of the error matrix, # so there is some overestimation). WARNING: For symbolic x, the resulting # polynomials have to be interpreted with some care: in particular, it would # be incorrect to evaluate a polynomial result with real coefficients at a # complex point. Our current mechanism to choose whether to add a real or # complex error bound in this case is pretty fragile. tail_bound = tail_bound.abs() res = vector(_add_error(psum[i], tail_bound) for i in xrange(ord)) return res
def radical_basis(self): r""" Return a basis of the Jacobson radical of this algebra. .. NOTE:: This implementation handles algebras over fields of characteristic zero (using Dixon's lemma) or fields of characteristic `p` in which we can compute `x^{1/p}` [FR1985]_, [Eb1989]_. OUTPUT: - a list of elements of ``self``. .. SEEALSO:: :meth:`radical`, :class:`Algebras.Semisimple` EXAMPLES:: sage: A = Algebras(QQ).FiniteDimensional().WithBasis().example(); A An example of a finite dimensional algebra with basis: the path algebra of the Kronecker quiver (containing the arrows a:x->y and b:x->y) over Rational Field sage: A.radical_basis() (a, b) We construct the group algebra of the Klein Four-Group over the rationals:: sage: A = KleinFourGroup().algebra(QQ) This algebra belongs to the category of finite dimensional algebras over the rationals:: sage: A in Algebras(QQ).FiniteDimensional().WithBasis() True Since the field has characteristic `0`, Maschke's Theorem tells us that the group algebra is semisimple. So its radical is the zero ideal:: sage: A in Algebras(QQ).Semisimple() True sage: A.radical_basis() () Let's work instead over a field of characteristic `2`:: sage: A = KleinFourGroup().algebra(GF(2)) sage: A in Algebras(GF(2)).Semisimple() False sage: A.radical_basis() (() + (1,2)(3,4), (3,4) + (1,2)(3,4), (1,2) + (1,2)(3,4)) We now implement the algebra `A = K[x] / (x^p-1)`, where `K` is a finite field of characteristic `p`, and check its radical; alas, we currently need to wrap `A` to make it a proper :class:`ModulesWithBasis`:: sage: class AnAlgebra(CombinatorialFreeModule): ....: def __init__(self, F): ....: R.<x> = PolynomialRing(F) ....: I = R.ideal(x**F.characteristic()-F.one()) ....: self._xbar = R.quotient(I).gen() ....: basis_keys = [self._xbar**i for i in range(F.characteristic())] ....: CombinatorialFreeModule.__init__(self, F, basis_keys, ....: category=Algebras(F).FiniteDimensional().WithBasis()) ....: def one(self): ....: return self.basis()[self.base_ring().one()] ....: def product_on_basis(self, w1, w2): ....: return self.from_vector(vector(w1*w2)) sage: AnAlgebra(GF(3)).radical_basis() (B[1] + 2*B[xbar^2], B[xbar] + 2*B[xbar^2]) sage: AnAlgebra(GF(16,'a')).radical_basis() (B[1] + B[xbar],) sage: AnAlgebra(GF(49,'a')).radical_basis() (B[1] + 6*B[xbar^6], B[xbar] + 6*B[xbar^6], B[xbar^2] + 6*B[xbar^6], B[xbar^3] + 6*B[xbar^6], B[xbar^4] + 6*B[xbar^6], B[xbar^5] + 6*B[xbar^6]) TESTS:: sage: A = KleinFourGroup().algebra(GF(2)) sage: A.radical_basis() (() + (1,2)(3,4), (3,4) + (1,2)(3,4), (1,2) + (1,2)(3,4)) sage: A = KleinFourGroup().algebra(QQ, category=Monoids()) sage: A.radical_basis.__module__ 'sage.categories.finite_dimensional_algebras_with_basis' sage: A.radical_basis() () """ F = self.base_ring() if not F.is_field(): raise NotImplementedError("the base ring must be a field") p = F.characteristic() from sage.matrix.constructor import matrix from sage.modules.free_module_element import vector product_on_basis = self.product_on_basis if p == 0: keys = list(self.basis().keys()) cache = [{(i, j): c for i in keys for j, c in product_on_basis(y, i)} for y in keys] mat = [[ sum(x.get((j, i), 0) * c for (i, j), c in y.items()) for x in cache ] for y in cache] mat = matrix(self.base_ring(), mat) rad_basis = mat.kernel().basis() else: # TODO: some finite field elements in Sage have both an # ``nth_root`` method and a ``pth_root`` method (such as ``GF(9,'a')``), # some only have a ``nth_root`` element such as ``GF(2)`` # I imagine that ``pth_root`` would be fastest, but it is not # always available.... if hasattr(self.base_ring().one(), 'nth_root'): root_fcn = lambda s, x: x.nth_root(s) else: root_fcn = lambda s, x: x**(1 / s) s, n = 1, self.dimension() B = [b.on_left_matrix() for b in self.basis()] I = B[0].parent().one() while s <= n: BB = B + [I] G = matrix([[ (-1)**s * (b * bb).characteristic_polynomial()[n - s] for bb in BB ] for b in B]) C = G.left_kernel().basis() if 1 < s < F.order(): C = [ vector(F, [root_fcn(s, ci) for ci in c]) for c in C ] B = [sum(ci * b for (ci, b) in zip(c, B)) for c in C] s = p * s e = vector(self.one()) rad_basis = [b * e for b in B] return tuple([self.from_vector(vec) for vec in rad_basis])
def _discrete_log_pgroup(self, p, aa, b): r""" Attempt to express an element of p-power order in terms of generators of a p-subgroup of this group. Used as a subroutine in the _discrete_log() method. ALGORITHM: This implements a basic version of the recursive algorithm from [Suth2008]_. The base cases are handled using a variant of Shanks' baby-step giant-step algorithm for products of cyclic groups. EXAMPLES:: sage: G = AdditiveAbelianGroup([5, 5**2, 5**4, 5**4]) sage: (a, b, c, d) = gs = G.gens() sage: A = AdditiveAbelianGroupWrapper(a.parent(), gs, [g.order() for g in gs]) sage: A._discrete_log_pgroup(5, gs, a + 17 * b + 123 * c + 456 * d) (1, 17, 123, 456) """ from sage.arith.misc import valuation from sage.functions.other import ceil from sage.misc.functional import sqrt from itertools import product as iproduct vals = [valuation(a.order(), p) for a in aa] qq = lambda j, k: vector(p**(j + max(0, v - k)) for a, v in zip(aa, vals)) subbasis = lambda j, k: [q * a for q, a in zip(qq(j, k), aa)] dotprod = lambda xs, ys: sum(x * y for x, y in zip(xs, ys)) def _base(j, k, c): assert k - j == 1 aajk = subbasis(j, k) assert all(a.order() in (1, p) for a in aajk) idxs = [i for i, a in enumerate(aajk) if a.order() == p] rs = [([0], [0]) for i in range(len(aajk))] for i in range(len(idxs)): rs[idxs[i]] = (range(p), [0]) if i % 2 else ([0], range(p)) if len(idxs) % 2: m = ceil(sqrt(p)) rs[idxs[-1]] = range(0, p, m), range(m) tab = {} for x in iproduct(*(r for r, _ in rs)): key = dotprod(x, aajk) if hasattr(key, 'set_immutable'): key.set_immutable() tab[key] = vector(x) for y in iproduct(*(r for _, r in rs)): key = c - dotprod(y, aajk) if hasattr(key, 'set_immutable'): key.set_immutable() if key in tab: return tab[key] + vector(y) raise TypeError('Not in group') def _rec(j, k, c): assert 0 <= j < k if k - j <= 1: # base case return _base(j, k, c) w = 2 js = list(range(j, k, (k - j + w - 1) // w)) + [k] assert len(js) == w + 1 x = vector([0] * len(aa)) for i in reversed(range(w)): gamma = p**(js[i] - j) * c - dotprod(x, subbasis(js[i], k)) v = _rec(js[i], js[i + 1], gamma) assert not any( q1 % q2 for q1, q2 in zip(qq(js[i], js[i + 1]), qq(js[i], k))) x += vector( q1 // q2 * r for q1, q2, r in zip(qq(js[i], js[i + 1]), qq(js[i], k), v)) return x return _rec(0, max(vals), b)
def extract(cls, obj): """ Takes an object extracted by the json parser and decodes the special-formating dictionaries used to store special types. """ if isinstance(obj, dict) and "data" in obj: if len(obj) == 2 and "__ComplexList__" in obj: return [complex(*v) for v in obj["data"]] elif len(obj) == 2 and "__QQList__" in obj: assert SAGE_MODE return [QQ(tuple(v)) for v in obj["data"]] elif len(obj) == 3 and "__NFList__" in obj and "base" in obj: assert SAGE_MODE base = cls.extract(obj["base"]) return [cls._extract(base, c) for c in obj["data"]] elif len(obj) == 2 and "__IntDict__" in obj: if SAGE_MODE: return {Integer(k): cls.extract(v) for k, v in obj["data"]} else: return {int(k): cls.extract(v) for k, v in obj["data"]} elif len(obj) == 3 and "__Vector__" in obj and "base" in obj: assert SAGE_MODE base = cls.extract(obj["base"]) return vector([cls._extract(base, v) for v in obj["data"]]) elif len(obj) == 2 and "__Rational__" in obj: assert SAGE_MODE return Rational(*obj["data"]) elif len(obj) == 3 and "__RealLiteral__" in obj and "prec" in obj: assert SAGE_MODE return LmfdbRealLiteral(RealField(obj["prec"]), obj["data"]) elif len(obj) == 2 and "__complex__" in obj: return complex(*obj["data"]) elif len(obj) == 3 and "__Complex__" in obj and "prec" in obj: assert SAGE_MODE return ComplexNumber(ComplexField(obj["prec"]), *obj["data"]) elif len(obj) == 3 and "__NFElt__" in obj and "parent" in obj: assert SAGE_MODE return cls._extract(cls.extract(obj["parent"]), obj["data"]) elif ( len(obj) == 3 and ("__NFRelative__" in obj or "__NFAbsolute__" in obj) and "vname" in obj ): assert SAGE_MODE poly = cls.extract(obj["data"]) return NumberField(poly, name=obj["vname"]) elif len(obj) == 2 and "__NFCyclotomic__" in obj: assert SAGE_MODE return CyclotomicField(obj["data"]) elif len(obj) == 2 and "__IntegerRing__" in obj: assert SAGE_MODE return ZZ elif len(obj) == 2 and "__RationalField__" in obj: assert SAGE_MODE return QQ elif len(obj) == 3 and "__RationalPoly__" in obj and "vname" in obj: assert SAGE_MODE return QQ[obj["vname"]]([QQ(tuple(v)) for v in obj["data"]]) elif (len(obj) == 4 and "__Poly__" in obj and "vname" in obj and "base" in obj): assert SAGE_MODE base = cls.extract(obj["base"]) return base[obj["vname"]]([cls._extract(base, c) for c in obj["data"]]) elif ( len(obj) == 5 and "__PowerSeries__" in obj and "vname" in obj and "base" in obj and "prec" in obj ): assert SAGE_MODE base = cls.extract(obj["base"]) prec = infinity if obj["prec"] == "inf" else int(obj["prec"]) return base[[obj["vname"]]]([cls._extract(base, c) for c in obj["data"]], prec=prec) elif len(obj) == 2 and "__date__" in obj: return datetime.datetime.strptime(obj["data"], "%Y-%m-%d").date() elif len(obj) == 2 and "__time__" in obj: return datetime.datetime.strptime(obj["data"], "%H:%M:%S.%f").time() elif len(obj) == 2 and "__datetime__" in obj: return datetime.datetime.strptime(obj["data"], "%Y-%m-%d %H:%M:%S.%f") return obj
def diamond_cut(V, GM, C, verbose=False): r""" Perform diamond cutting on polyhedron ``V`` with basis matrix ``GM`` and radius ``C``. INPUT: - ``V`` -- polyhedron to cut from - ``GM`` -- half of the basis matrix of the lattice - ``C`` -- radius to use in cutting algorithm - ``verbose`` -- (default: ``False``) whether to print debug information OUTPUT: A :class:``Polyhedron`` instance. EXAMPLES:: sage: from sage.modules.diamond_cutting import diamond_cut sage: V = Polyhedron([[0], [2]]) sage: GM = matrix([2]) sage: V = diamond_cut(V, GM, 4) sage: V.vertices() (A vertex at (2), A vertex at (0)) """ # coerce to floats GM = GM.N() C = float(C) if verbose: print("Cut\n{}\nwith radius {}".format(GM, C)) dim = GM.dimensions() if dim[0] != dim[1]: raise ValueError("the matrix must be square") dim = dim[0] T = [0] * dim U = [0] * dim x = [0] * dim L = [0] * dim # calculate the Gram matrix q = matrix( [[sum(GM[i][k] * GM[j][k] for k in range(dim)) for j in range(dim)] for i in range(dim)]) if verbose: print("q:\n{}".format(q.N())) # apply Cholesky/Jacobi decomposition q = jacobi(q) if verbose: print("q:\n{}".format(q.N())) i = dim - 1 T[i] = C U[i] = 0 new_dimension = True cut_count = 0 inequalities = [] while True: if verbose: print("Dimension: {}".format(i)) if new_dimension: Z = sqrt(T[i] / q[i][i]) if verbose: print("Z: {}".format(Z)) L[i] = int(floor(Z - U[i])) if verbose: print("L: {}".format(L)) x[i] = int(ceil(-Z - U[i]) - 1) new_dimension = False x[i] += 1 if verbose: print("x: {}".format(x)) if x[i] > L[i]: i += 1 elif i > 0: T[i - 1] = T[i] - q[i][i] * (x[i] + U[i])**2 i -= 1 U[i] = 0 for j in range(i + 1, dim): U[i] += q[i][j] * x[j] new_dimension = True else: if all(elmt == 0 for elmt in x): break hv = [0] * dim for k in range(dim): for j in range(dim): hv[k] += x[j] * GM[j][k] hv = vector(hv) for hv in [hv, -hv]: cut_count += 1 if verbose: print "\n%d) Cut using normal vector %s" % (cut_count, hv) hv = [QQ(round(elmt, 6)) for elmt in hv] inequalities.append(plane_inequality(hv)) #cut = Polyhedron(ieqs=[plane_inequality(hv)]) #V = V.intersection(cut) if verbose: print("Final cut") cut = Polyhedron(ieqs=inequalities) V = V.intersection(cut) if verbose: print("End") return V
def has_singular_point(self, point=False): r""" Return True if and only if the conic ``self`` has a rational singular point. If ``point`` is True, then also return a rational singular point (or ``None`` if no such point exists). EXAMPLES: :: sage: c = Conic(QQ, [1,0,1]); c Projective Conic Curve over Rational Field defined by x^2 + z^2 sage: c.has_singular_point(point = True) (True, (0 : 1 : 0)) sage: P.<x,y,z> = GF(7)[] sage: e = Conic((x+y+z)*(x-y+2*z)); e Projective Conic Curve over Finite Field of size 7 defined by x^2 - y^2 + 3*x*z + y*z + 2*z^2 sage: e.has_singular_point(point = True) (True, (2 : 4 : 1)) sage: Conic([1, 1, -1]).has_singular_point() False sage: Conic([1, 1, -1]).has_singular_point(point = True) (False, None) ``has_singular_point`` is not implemented over all fields of characteristic `2`. It is implemented over finite fields. :: sage: F.<a> = FiniteField(8) sage: Conic([a, a+1, 1]).has_singular_point(point = True) (True, (a + 1 : 0 : 1)) sage: P.<t> = GF(2)[] sage: C = Conic(P, [t,t,1]); C Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 2 (using NTL) defined by t*x^2 + t*y^2 + z^2 sage: C.has_singular_point(point = False) Traceback (most recent call last): ... NotImplementedError: Sorry, find singular point on conics not implemented over all fields of characteristic 2. """ if not point: ret = self.has_singular_point(point=True) return ret[0] B = self.base_ring() if B.characteristic() == 2: [a, b, c, d, e, f] = self.coefficients() if b == 0 and c == 0 and e == 0: for i in range(3): if [a, d, f][i] == 0: return True, self.point(vector(B, {2: 0, i: 1})) if hasattr(a / f, 'is_square') and hasattr(a / f, 'sqrt'): if (a / f).is_square(): return True, self.point([1, 0, (a / f).sqrt()]) if (d / f).is_square(): return True, self.point([0, 1, (d / f).sqrt()]) raise NotImplementedError, "Sorry, find singular point on conics not implemented over all fields of characteristic 2." pt = [e, c, b] if self.defining_polynomial()(pt) == 0: return True, self.point(pt) return False, None D = self.symmetric_matrix() if D.determinant() == 0: return True, self.point(Sequence(D.right_kernel().gen())) return False, None
def has_rational_point(self, point=False, algorithm='default', read_cache=True): r""" Returns True if and only if the conic ``self`` has a point over its base field `B`. If ``point`` is True, then returns a second output, which is a rational point if one exists. Points are cached whenever they are found. Cached information is used if and only if ``read_cache`` is True. ALGORITHM: The parameter ``algorithm`` specifies the algorithm to be used: - ``'default'`` -- If the base field is real or complex, use an elementary native Sage implementation. - ``'magma'`` (requires Magma to be installed) -- delegates the task to the Magma computer algebra system. EXAMPLES:: sage: Conic(RR, [1, 1, 1]).has_rational_point() False sage: Conic(CC, [1, 1, 1]).has_rational_point() True sage: Conic(RR, [1, 2, -3]).has_rational_point(point = True) (True, (1.73205080756888 : 0.000000000000000 : 1.00000000000000)) Conics over polynomial rings can be solved internally:: sage: R.<t> = QQ[] sage: C = Conic([-2,t^2+1,t^2-1]) sage: C.has_rational_point() True And they can also be solved with Magma:: sage: C.has_rational_point(algorithm='magma') # optional - magma True sage: C.has_rational_point(algorithm='magma', point=True) # optional - magma (True, (-t : 1 : 1)) sage: D = Conic([t,1,t^2]) sage: D.has_rational_point(algorithm='magma') # optional - magma False TESTS: One of the following fields comes with an embedding into the complex numbers, one does not. Check that they are both handled correctly by the Magma interface. :: sage: K.<i> = QuadraticField(-1) sage: K.coerce_embedding() Generic morphism: From: Number Field in i with defining polynomial x^2 + 1 with i = 1*I To: Complex Lazy Field Defn: i -> 1*I sage: Conic(K, [1,1,1]).rational_point(algorithm='magma') # optional - magma (-i : 1 : 0) sage: x = QQ['x'].gen() sage: L.<i> = NumberField(x^2+1, embedding=None) sage: Conic(L, [1,1,1]).rational_point(algorithm='magma') # optional - magma (-i : 1 : 0) sage: L == K False """ if read_cache: if self._rational_point is not None: if point: return True, self._rational_point else: return True B = self.base_ring() if algorithm == 'magma': from sage.interfaces.magma import magma M = magma(self) b = M.HasRationalPoint().sage() if not point: return b if not b: return False, None M_pt = M.HasRationalPoint(nvals=2)[1] # Various attempts will be made to convert `pt` to # a Sage object. The end result will always be checked # by self.point(). pt = [M_pt[1], M_pt[2], M_pt[3]] # The first attempt is to use sequences. This is efficient and # succeeds in cases where the Magma interface fails to convert # number field elements, because embeddings between number fields # may be lost on conversion to and from Magma. # This should deal with all absolute number fields. try: return True, self.point([B(c.Eltseq().sage()) for c in pt]) except TypeError: pass # The second attempt tries to split Magma elements into # numerators and denominators first. This is necessary # for the field of rational functions, because (at the moment of # writing) fraction field elements are not converted automatically # from Magma to Sage. try: return True, self.point( \ [B(c.Numerator().sage()/c.Denominator().sage()) for c in pt]) except (TypeError, NameError): pass # Finally, let the Magma interface handle conversion. try: return True, self.point([B(c.sage()) for c in pt]) except (TypeError, NameError): pass raise NotImplementedError( "No correct conversion implemented for converting the Magma point %s on %s to a correct Sage point on self (=%s)" % (M_pt, M, self)) if algorithm != 'default': raise ValueError("Unknown algorithm: %s" % algorithm) if is_ComplexField(B): if point: [_, _, _, d, e, f] = self._coefficients if d == 0: return True, self.point([0, 1, 0]) return True, self.point( [0, ((e**2 - 4 * d * f).sqrt() - e) / (2 * d), 1], check=False) return True if is_RealField(B): D, T = self.diagonal_matrix() [a, b, c] = [D[0, 0], D[1, 1], D[2, 2]] if a == 0: ret = True, self.point(T * vector([1, 0, 0]), check=False) elif a * c <= 0: ret = True, self.point(T * vector([(-c / a).sqrt(), 0, 1]), check=False) elif b == 0: ret = True, self.point(T * vector([0, 1, 0]), check=False) elif b * c <= 0: ret = True, self.point(T * vector([0, (-c / b).sqrt(), 0, 1]), check=False) else: ret = False, None if point: return ret return ret[0] raise NotImplementedError("has_rational_point not implemented for " \ "conics over base field %s" % B)
def to_surface(self, point, v=None, label=None, ring=None, return_all=False, \ singularity_limit=None, search_all = False, search_limit=None): r""" Converts from graphical coordinates to similarity surface coordinates. A point always must be provided. If a vector v is provided then a SimilaritySurfaceTangentVector will be returned. If v is not provided, then a SurfacePoint is returned. INPUT: - ``point`` -- Coordinates of a point in graphical coordinates to be converted to graphical coordinates. - ``v`` -- (default ``None``) If provided a tangent vector in graphical coordinates based at the provided point. - ``label`` -- (default ``None``) If provided, then we only convert points and tangent vectors in the corresponding graphical polygon. - ``ring`` -- (default ``None``) If provided, then objects returned will be defined over the given ring, otherwise we use the base_ring of the surface. - ``return_all`` -- (default ``False``) By default we return the first point or vector we find. However if the graphical polygons overlap, then a point or vector might correspond to more than one point or vector on the surface. If ``return_all`` is set to ``True`` then we return a set of all points we find instead. - ``singularity_limit`` -- (default ``None``) This only has an effect if returning a singular point (i.e., ``v`` is ``None``) and the surface is infinite. In this case, the singularity should be returned but it could be infinite. Then singularity_limit controls how far we look for the singularity to close. This value is passed to ``SimilaritySurface.surface_point``. - ``search_all`` -- (default ``False``) By default we look just in polygons with visible label. If set to `True``, then we instead look in all labels. - ``search_limit`` -- (default ``None``) If ``search_all`` is ``True``, then we look at the first ``search_limit`` polygons instead of all polygons. This must be set to an positive integer if ``search_all`` is and the surface is infinite. EXAMPLES:: sage: from flatsurf import * sage: s = similarity_surfaces.example() sage: gs = s.graphical_surface() sage: gs.to_surface((1,-2)) Surface point located at (1, 1/2) in polygon 1 sage: gs.to_surface((1,-2), v=(1,0)) SimilaritySurfaceTangentVector in polygon 1 based at (1, 1/2) with vector (1, -1/2) sage: s = translation_surfaces.infinite_staircase() sage: gs = s.graphical_surface() sage: gs.to_surface((4,4), (1,1), search_all=True, search_limit=20) SimilaritySurfaceTangentVector in polygon 8 based at (0, 0) with vector (1, 1) sage: s = translation_surfaces.square_torus() sage: pc = s.minimal_cover(cover_type="planar") sage: gs = pc.graphical_surface() sage: gs.to_surface((3,2), search_all=True, search_limit=20) Traceback (most recent call last): ... ValueError: To obtain a singularity on an infinite surface, singularity_limit must be set. sage: gs.to_surface((3,2), search_all=True, search_limit=20, singularity_limit=4) Surface point with 4 coordinate representations sage: p = gs.to_surface((sqrt(3),sqrt(2)), ring=AA, search_all=True, search_limit=20) sage: next(iter(p.coordinates(p.labels()[0]))).parent() Vector space of dimension 2 over Algebraic Real Field sage: v = gs.to_surface((3/2,3/2),(sqrt(3),sqrt(2)),ring=AA,search_all=True, search_limit=20) sage: v.bundle() Tangent bundle of TranslationSurface built from infinitely many polygons defined over Algebraic Real Field """ if label is None: if return_all: ret = set() s = self.get_surface() if search_all: if search_limit is None: if s.is_finite(): it = s.label_iterator() else: raise ValueError( "If search_all=True and the surface is infinite, then a search_limit must be provided." ) else: from itertools import islice it = islice(s.label_iterator(), search_limit) else: it = self.visible() for label in it: try: val = self.to_surface(point, v=v, label=label, ring=ring, singularity_limit=singularity_limit) if return_all: ret.add(val) else: return val except AssertionError: # Not in the polygon pass except ValueError as e: if e.args[ 0] == 'need a limit when working with an infinite surface': raise ValueError("To obtain a singularity on an infinite surface, " + \ "singularity_limit must be set.") # Otherwise it is not in the polygon. if return_all: return ret else: raise ValueError( "Point or vector is not in a visible graphical_polygon.") else: gp = self.graphical_polygon(label) coords = gp.transform_back(point) s = self.get_surface() if v is None: return s.surface_point(label, coords, ring=ring, limit=singularity_limit) else: return s.tangent_vector( label, coords, (~(gp.transformation().derivative())) * vector(v), ring=ring)
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 hom(self, x, Y=None): r""" Return the scheme morphism from ``self`` to ``Y`` defined by ``x``. Here ``x`` can be a matrix or a sequence of polynomials. If ``Y`` is omitted, then a natural image is found if possible. EXAMPLES: Here are a few Morphisms given by matrices. In the first example, ``Y`` is omitted, in the second example, ``Y`` is specified. :: sage: c = Conic([-1, 1, 1]) sage: h = c.hom(Matrix([[1,1,0],[0,1,0],[0,0,1]])); h Scheme morphism: From: Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2 To: Projective Conic Curve over Rational Field defined by -x^2 + 2*x*y + z^2 Defn: Defined on coordinates by sending (x : y : z) to (x + y : y : z) sage: h([-1, 1, 0]) (0 : 1 : 0) sage: c = Conic([-1, 1, 1]) sage: d = Conic([4, 1, -1]) sage: c.hom(Matrix([[0, 0, 1/2], [0, 1, 0], [1, 0, 0]]), d) Scheme morphism: From: Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2 To: Projective Conic Curve over Rational Field defined by 4*x^2 + y^2 - z^2 Defn: Defined on coordinates by sending (x : y : z) to (1/2*z : y : x) ``ValueError`` is raised if the wrong codomain ``Y`` is specified: :: sage: c = Conic([-1, 1, 1]) sage: c.hom(Matrix([[0, 0, 1/2], [0, 1, 0], [1, 0, 0]]), c) Traceback (most recent call last): ... ValueError: The matrix x (= [ 0 0 1/2] [ 0 1 0] [ 1 0 0]) does not define a map from self (= Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2) to Y (= Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2) """ if is_Matrix(x): from constructor import Conic y = x.inverse() A = y.transpose() * self.matrix() * y im = Conic(A) if Y == None: Y = im else: q = Y.defining_polynomial() / im.defining_polynomial() if not (q.numerator().is_constant() and q.denominator().is_constant()): raise ValueError, "The matrix x (= %s) does not define a " \ "map from self (= %s) to Y (= %s)" % \ (x, self, Y) x = Sequence(x * vector(self.ambient_space().gens())) return self.Hom(Y)(x, check=False) return ProjectiveCurve_generic.hom(self, x, Y)
def minimize_constrained(func, cons, x0, gradient=None, algorithm='default', **args): r""" Minimize a function with constraints. INPUT: - ``func`` -- Either a symbolic function, or a Python function whose argument is a tuple with n components - ``cons`` -- constraints. This should be either a function or list of functions that must be positive. Alternatively, the constraints can be specified as a list of intervals that define the region we are minimizing in. If the constraints are specified as functions, the functions should be functions of a tuple with `n` components (assuming `n` variables). If the constraints are specified as a list of intervals and there are no constraints for a given variable, that component can be (``None``, ``None``). - ``x0`` -- Initial point for finding minimum - ``algorithm`` -- Optional, specify the algorithm to use: - ``'default'`` -- default choices - ``'l-bfgs-b'`` -- only effective if you specify bound constraints. See [ZBN97]_. - ``gradient`` -- Optional gradient function. This will be computed automatically for symbolic functions. This is only used when the constraints are specified as a list of intervals. EXAMPLES: Let us maximize `x + y - 50` subject to the following constraints: `50x + 24y \leq 2400`, `30x + 33y \leq 2100`, `x \geq 45`, and `y \geq 5`:: sage: y = var('y') sage: f = lambda p: -p[0]-p[1]+50 sage: c_1 = lambda p: p[0]-45 sage: c_2 = lambda p: p[1]-5 sage: c_3 = lambda p: -50*p[0]-24*p[1]+2400 sage: c_4 = lambda p: -30*p[0]-33*p[1]+2100 sage: a = minimize_constrained(f,[c_1,c_2,c_3,c_4],[2,3]) sage: a (45.0, 6.25) Let's find a minimum of `\sin(xy)`:: sage: x,y = var('x y') sage: f = sin(x*y) sage: minimize_constrained(f, [(None,None),(4,10)],[5,5]) (4.8..., 4.8...) Check, if L-BFGS-B finds the same minimum:: sage: minimize_constrained(f, [(None,None),(4,10)],[5,5], algorithm='l-bfgs-b') (4.7..., 4.9...) Rosenbrock function, [http://en.wikipedia.org/wiki/Rosenbrock_function]:: sage: from scipy.optimize import rosen, rosen_der sage: minimize_constrained(rosen, [(-50,-10),(5,10)],[1,1],gradient=rosen_der,algorithm='l-bfgs-b') (-10.0, 10.0) sage: minimize_constrained(rosen, [(-50,-10),(5,10)],[1,1],algorithm='l-bfgs-b') (-10.0, 10.0) REFERENCES: .. [ZBN97] C. Zhu, R. H. Byrd and J. Nocedal. L-BFGS-B: Algorithm 778: L-BFGS-B, FORTRAN routines for large scale bound constrained optimization. ACM Transactions on Mathematical Software, Vol 23, Num. 4, pp.550--560, 1997. """ from sage.symbolic.expression import Expression import scipy from scipy import optimize function_type = type(lambda x, y: x + y) if isinstance(func, Expression): var_list = func.variables() var_names = map(str, var_list) fast_f = func._fast_float_(*var_names) f = lambda p: fast_f(*p) gradient_list = func.gradient() fast_gradient_functions = [ gradient_list[i]._fast_float_(*var_names) for i in xrange(len(gradient_list)) ] gradient = lambda p: scipy.array( [a(*p) for a in fast_gradient_functions]) else: f = func if isinstance(cons, list): if isinstance(cons[0], tuple) or isinstance(cons[0], list) or cons[0] == None: if gradient != None: if algorithm == 'l-bfgs-b': min = optimize.fmin_l_bfgs_b(f, x0, gradient, bounds=cons, iprint=-1, **args)[0] else: min = optimize.fmin_tnc(f, x0, gradient, bounds=cons, messages=0, **args)[0] else: if algorithm == 'l-bfgs-b': min = optimize.fmin_l_bfgs_b(f, x0, approx_grad=True, bounds=cons, iprint=-1, **args)[0] else: min = optimize.fmin_tnc(f, x0, approx_grad=True, bounds=cons, messages=0, **args)[0] elif isinstance(cons[0], function_type): min = optimize.fmin_cobyla(f, x0, cons, iprint=0, **args) elif isinstance(cons, function_type): min = optimize.fmin_cobyla(f, x0, cons, iprint=0, **args) return vector(RDF, min)
def __call__(self, v): """ Apply the affine transformation to ``v``. INPUT: - ``v`` -- a polynomial, a multivariate polynomial, a polyhedron, a vector, or anything that can be converted into a vector. OUTPUT: The image of ``v`` under the affine group element. EXAMPLES:: sage: G = AffineGroup(2, QQ) sage: g = G([0,1,-1,0],[2,3]); g [ 0 1] [2] x |-> [-1 0] x + [3] sage: v = vector([4,5]) sage: g(v) (7, -1) sage: R.<x,y> = QQ[] sage: g(x), g(y) (y + 2, -x + 3) sage: p = x^2 + 2*x*y + y + 1 sage: g(p) -2*x*y + y^2 - 5*x + 10*y + 20 The action on polynomials is such that it intertwines with evaluation. That is:: sage: p(*g(v)) == g(p)(*v) True Test that the univariate polynomial ring is covered:: sage: H = AffineGroup(1, QQ) sage: h = H([2],[3]); h x |-> [2] x + [3] sage: R.<z> = QQ[] sage: h(z+1) 3*z + 2 The action on a polyhedron is defined (see :trac:`30327`):: sage: F = AffineGroup(3, QQ) sage: M = matrix(3, [-1, -2, 0, 0, 0, 1, -2, 1, -1]) sage: v = vector(QQ,(1,2,3)) sage: f = F(M, v) sage: cube = polytopes.cube() sage: f(cube) A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices """ parent = self.parent() # start with the most probable case, i.e., v is in the vector space if v in parent.vector_space(): return self._A * v + self._b from sage.rings.polynomial.polynomial_element import is_Polynomial if is_Polynomial(v) and parent.degree() == 1: ring = v.parent() return ring([self._A[0, 0], self._b[0]]) from sage.rings.polynomial.multi_polynomial import is_MPolynomial if is_MPolynomial(v) and parent.degree() == v.parent().ngens(): ring = v.parent() from sage.modules.free_module_element import vector image_coords = self._A * vector(ring, ring.gens()) + self._b return v(*image_coords) import sage.geometry.abc if isinstance(v, sage.geometry.abc.Polyhedron): return self._A * v + self._b # otherwise, coerce v into the vector space v = parent.vector_space()(v) return self._A * v + self._b
def reynolds_operator(self, poly, chi=None): r""" Compute the Reynolds operator of this finite group `G`. This is the projection from a polynomial ring to the ring of relative invariants [Stu1993]_. If possible, the invariant is returned defined over the base field of the given polynomial ``poly``, otherwise, it is returned over the compositum of the fields involved in the computation. Only implemented for absolute fields. ALGORITHM: Let `K[x]` be a polynomial ring and `\chi` a linear character for `G`. Let .. MATH: K[x]^G_{\chi} = \{f \in K[x] | \pi f = \chi(\pi) f \forall \pi\in G\} be the ring of invarants of `G` relative to `\chi`. Then the Reynold's operator is a map `R` from `K[x]` into `K[x]^G_{\chi}` defined by .. MATH: f \mapsto \frac{1}{|G|} \sum_{ \pi \in G} \chi(\pi) f. INPUT: - ``poly`` -- a polynomial - ``chi`` -- (default: trivial character) a linear group character of this group OUTPUT: an invariant polynomial relative to `\chi` AUTHORS: Rebecca Lauren Miller and Ben Hutz EXAMPLES:: sage: S3 = MatrixGroup(SymmetricGroup(3)) sage: R.<x,y,z> = QQ[] sage: f = x*y*z^3 sage: S3.reynolds_operator(f) 1/3*x^3*y*z + 1/3*x*y^3*z + 1/3*x*y*z^3 :: sage: G = MatrixGroup(CyclicPermutationGroup(4)) sage: chi = G.character(G.character_table()[3]) sage: K.<v> = CyclotomicField(4) sage: R.<x,y,z,w> = K[] sage: G.reynolds_operator(x, chi) 1/4*x + (1/4*v)*y - 1/4*z + (-1/4*v)*w sage: chi = G.character(G.character_table()[2]) sage: R.<x,y,z,w> = QQ[] sage: G.reynolds_operator(x*y, chi) 1/4*x*y + (-1/4*zeta4)*y*z + (1/4*zeta4)*x*w - 1/4*z*w :: sage: K.<i> = CyclotomicField(4) sage: G = MatrixGroup(CyclicPermutationGroup(3)) sage: chi = G.character(G.character_table()[1]) sage: R.<x,y,z> = K[] sage: G.reynolds_operator(x*y^5, chi) 1/3*x*y^5 + (2/3*izeta3^3 + izeta3^2 + 8/3*izeta3 + 1)*x^5*z + (-2/3*izeta3^3 - izeta3^2 - 8/3*izeta3 - 4/3)*y*z^5 sage: R.<x,y,z> = QQbar[] sage: G.reynolds_operator(x*y^5, chi) 1/3*x*y^5 + (-0.1666666666666667? - 0.2886751345948129?*I)*x^5*z + (-0.1666666666666667? + 0.2886751345948129?*I)*y*z^5 :: sage: K.<i> = CyclotomicField(4) sage: Tetra = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0]) sage: chi = Tetra.character(Tetra.character_table()[4]) sage: L.<v> = QuadraticField(-3) sage: R.<x,y> = L[] sage: Tetra.reynolds_operator(x^4) 0 sage: Tetra.reynolds_operator(x^4, chi) 1/4*x^4 + (1/2*v)*x^2*y^2 + 1/4*y^4 sage: R.<x>=L[] sage: LL.<w> = L.extension(x^2+v) sage: R.<x,y> = LL[] sage: Tetra.reynolds_operator(x^4, chi) Traceback (most recent call last): ... NotImplementedError: only implemented for absolute fields :: sage: G = MatrixGroup(DihedralGroup(4)) sage: chi = G.character(G.character_table()[1]) sage: R.<x,y> = QQ[] sage: f = x^4 sage: G.reynolds_operator(f, chi) Traceback (most recent call last): ... TypeError: number of variables in polynomial must match size of matrices sage: R.<x,y,z,w> = QQ[] sage: f = x^3*y sage: G.reynolds_operator(f, chi) 1/8*x^3*y - 1/8*x*y^3 + 1/8*y^3*z - 1/8*y*z^3 - 1/8*x^3*w + 1/8*z^3*w + 1/8*x*w^3 - 1/8*z*w^3 Characteristic p>0 examples:: sage: G = MatrixGroup([[0,1,1,0]]) sage: R.<w,x> = GF(2)[] sage: G.reynolds_operator(x) Traceback (most recent call last): ... NotImplementedError: not implemented when characteristic divides group order :: sage: i = GF(7)(3) sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]]) sage: chi = G.character(G.character_table()[4]) sage: R.<w,x> = GF(7)[] sage: f = w^5*x + x^6 sage: G.reynolds_operator(f, chi) Traceback (most recent call last): ... NotImplementedError: nontrivial characters not implemented for characteristic > 0 sage: G.reynolds_operator(f) x^6 :: sage: K = GF(3^2,'t') sage: G = MatrixGroup([matrix(K,2,2, [0,K.gen(),1,0])]) sage: R.<x,y> = GF(3)[] sage: G.reynolds_operator(x^8) -x^8 - y^8 :: sage: K = GF(3^2,'t') sage: G = MatrixGroup([matrix(GF(3),2,2, [0,1,1,0])]) sage: R.<x,y> = K[] sage: f = -K.gen()*x sage: G.reynolds_operator(f) (t)*x + (t)*y """ if poly.parent().ngens() != self.degree(): raise TypeError( "number of variables in polynomial must match size of matrices" ) R = FractionField(poly.base_ring()) C = FractionField(self.base_ring()) if chi is None: #then this is the trivial character if R.characteristic() == 0: #non-modular case if C == QQbar or R == QQbar: L = QQbar elif not C.is_absolute() or not R.is_absolute(): raise NotImplementedError( "only implemented for absolute fields") else: #create the compositum if C.absolute_degree() == 1: L = R elif R.absolute_degree() == 1: L = C else: L = C.composite_fields(R)[0] elif not R.characteristic().divides(self.order()): if R.characteristic() != C.characteristic(): raise ValueError( "base fields must have same characteristic") else: if R.degree() >= C.degree(): L = R else: L = C else: raise NotImplementedError( "not implemented when characteristic divides group order") poly = poly.change_ring(L) poly_gens = vector(poly.parent().gens()) F = L.zero() for g in self: F += poly(*g.matrix() * vector(poly.parent().gens())) F /= self.order() return F #non-trivial character case K = chi.values()[0].parent() if R.characteristic() == 0: #extend base_ring to compositum if C == QQbar or K == QQbar or R == QQbar: L = QQbar elif not C.is_absolute() or not K.is_absolute( ) or not R.is_absolute(): raise NotImplementedError( "only implemented for absolute fields") else: fields = [] for M in [R, K, C]: if M.absolute_degree() != 1: fields.append(M) l = len(fields) if l == 0: # all are QQ L = R elif l == 1: #only one is an extension L = fields[0] elif l == 2: #only two are extensions L = fields[0].composite_fields(fields[1])[0] else: #all three are extensions L1 = fields[0].composite_fields(fields[1])[0] L = L1.composite_fields(fields[2])[0] else: raise NotImplementedError( "nontrivial characters not implemented for characteristic > 0") poly = poly.change_ring(L) poly_gens = vector(poly.parent().gens()) F = L.zero() for g in self: F += L(chi(g)) * poly(*g.matrix().change_ring(L) * poly_gens) F /= self.order() try: # attempt to move F to base_ring of polyomial F = F.change_ring(R) except (TypeError, ValueError): pass return F
def repr_pretty(coefficients, type, prefix='x', indices=None, latex=False, style='>=', split=False): r""" Return a pretty representation of equation/inequality represented by the coefficients. INPUT: - ``coefficients`` -- a tuple or other iterable - ``type`` -- either ``0`` (``PolyhedronRepresentation.INEQUALITY``) or ``1`` (``PolyhedronRepresentation.EQUATION``) - ``prefix`` -- a string - ``indices`` -- a tuple or other iterable - ``latex`` -- a boolean - ``split`` -- a boolean; (Default: ``False``). If set to ``True``, the output is split into a 3-tuple containing the left-hand side, the relation, and the right-hand side of the object. - ``style`` -- either ``"positive"`` (making all coefficients positive), or ``"<="`` or ``">="``. OUTPUT: A string or 3-tuple of strings (depending on ``split``). EXAMPLES:: sage: from sage.geometry.polyhedron.representation import repr_pretty sage: from sage.geometry.polyhedron.representation import PolyhedronRepresentation sage: print(repr_pretty((0, 1, 0, 0), PolyhedronRepresentation.INEQUALITY)) x0 >= 0 sage: print(repr_pretty((1, 2, 1, 0), PolyhedronRepresentation.INEQUALITY)) 2*x0 + x1 >= -1 sage: print(repr_pretty((1, -1, -1, 1), PolyhedronRepresentation.EQUATION)) -x0 - x1 + x2 == -1 """ from sage.misc.latex import latex as latex_function from sage.modules.free_module_element import vector from sage.symbolic.ring import SR coeffs = vector(coefficients) if indices is None: indices = range(len(coeffs) - 1) vars = vector([1] + list(SR(prefix + '{}'.format(i)) for i in indices)) f = latex_function if latex else repr if type == PolyhedronRepresentation.EQUATION: rel = '=' if latex else '==' elif type == PolyhedronRepresentation.INEQUALITY: if style == '<=': rel = r'\leq' if latex else '<=' else: rel = r'\geq' if latex else '>=' else: raise NotImplementedError( 'no pretty printing available: wrong type {}'.format(type)) if style == 'positive': pos_part = vector([max(c, 0) for c in coeffs]) neg_part = pos_part - coeffs assert coeffs == pos_part - neg_part left_part = f(pos_part * vars) right_part = f(neg_part * vars) elif style == '>=': left_part = f(coeffs[1:] * vars[1:]) right_part = f(-coeffs[0]) elif style == '<=': left_part = f(-coeffs[1:] * vars[1:]) right_part = f(coeffs[0]) else: raise NotImplementedError( 'no pretty printing available: wrong style {}'.format(style)) if not split: return '{} {} {}'.format(left_part, rel, right_part) else: return (str(left_part), rel, str(right_part))
def ruler(start, end, ticks=4, sub_ticks=4, absolute=False, snap=False, **kwds): """ Draw a ruler in 3-D, with major and minor ticks. INPUT: - ``start`` -- the beginning of the ruler, as a list, tuple, or vector. - ``end`` -- the end of the ruler, as a list, tuple, or vector. - ``ticks`` -- (default: 4) the number of major ticks shown on the ruler. - ``sub_ticks`` -- (default: 4) the number of shown subdivisions between each major tick. - ``absolute`` -- (default: ``False``) if ``True``, makes a huge ruler in the direction of an axis. - ``snap`` -- (default: ``False``) if ``True``, snaps to an implied grid. EXAMPLES: A ruler:: sage: from sage.plot.plot3d.shapes2 import ruler sage: R = ruler([1,2,3],vector([2,3,4])); R Graphics3d Object A ruler with some options:: sage: R = ruler([1,2,3],vector([2,3,4]),ticks=6, sub_ticks=2, color='red'); R Graphics3d Object The keyword ``snap`` makes the ticks not necessarily coincide with the ruler:: sage: ruler([1,2,3],vector([1,2,4]),snap=True) Graphics3d Object The keyword ``absolute`` makes a huge ruler in one of the axis directions:: sage: ruler([1,2,3],vector([1,2,4]),absolute=True) Graphics3d Object TESTS:: sage: ruler([1,2,3],vector([1,3,4]),absolute=True) Traceback (most recent call last): ... ValueError: Absolute rulers only valid for axis-aligned paths """ start = vector(RDF, start) end = vector(RDF, end) dir = end - start dist = math.sqrt(dir.dot_product(dir)) dir /= dist one_tick = dist / ticks * 1.414 unit = 10**math.floor(math.log(dist / ticks, 10)) if unit * 5 < one_tick: unit *= 5 elif unit * 2 < one_tick: unit *= 2 if dir[0]: tick = dir.cross_product(vector(RDF, (0, 0, -dist / 30))) elif dir[1]: tick = dir.cross_product(vector(RDF, (0, 0, dist / 30))) else: tick = vector(RDF, (dist / 30, 0, 0)) if snap: for i in range(3): start[i] = unit * math.floor(start[i] / unit + 1e-5) end[i] = unit * math.ceil(end[i] / unit - 1e-5) if absolute: if dir[0] * dir[1] or dir[1] * dir[2] or dir[0] * dir[2]: raise ValueError( "Absolute rulers only valid for axis-aligned paths") m = max(dir[0], dir[1], dir[2]) if dir[0] == m: off = start[0] elif dir[1] == m: off = start[1] else: off = start[2] first_tick = unit * math.ceil(off / unit - 1e-5) - off else: off = 0 first_tick = 0 ruler = shapes.LineSegment(start, end, **kwds) for k in range(1, int(sub_ticks * first_tick / unit)): P = start + dir * (k * unit / sub_ticks) ruler += shapes.LineSegment(P, P + tick / 2, **kwds) for d in srange(first_tick, dist + unit / (sub_ticks + 1), unit): P = start + dir * d ruler += shapes.LineSegment(P, P + tick, **kwds) ruler += shapes.Text(str(d + off), **kwds).translate(P - tick) if dist - d < unit: sub_ticks = int(sub_ticks * (dist - d) / unit) for k in range(1, sub_ticks): P += dir * (unit / sub_ticks) ruler += shapes.LineSegment(P, P + tick / 2, **kwds) return ruler
def _element_constructor_(self, x, check=True): r""" Defines coercions and conversions. INPUT: - ``x`` -- an element of this group, a GAP element EXAMPLES:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3]) sage: A = AbelianGroup([2,3]) sage: a = A.an_element() sage: a f0*f1 sage: G(a) f1*f2 sage: A = AdditiveAbelianGroup([2,3]) sage: a = A.an_element() sage: a (1, 0) sage: G(a) f1 For general ``fgp_modules`` conversion is implemented if our group is in Smith form:: sage: G = AbelianGroupGap([6]) sage: A = ZZ^2 sage: e0,e1 = A.gens() sage: A = A / A.submodule([2*e0, 3*e1]) sage: a = 2 * A.an_element() sage: a (2) sage: G(a) f2 """ if isinstance(x, AbelianGroupElement_gap): x = x.gap() elif x == 1 or x == (): x = self.gap().Identity() elif not isinstance(x, GapElement): from sage.groups.abelian_gps.abelian_group_element import AbelianGroupElement from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroupElement from sage.modules.fg_pid.fgp_element import FGP_Element if isinstance(x, AbelianGroupElement): exp = x.exponents() elif isinstance(x, AdditiveAbelianGroupElement): exp = x._hermite_lift() elif isinstance(x, FGP_Element): exp = x.vector() else: from sage.modules.free_module_element import vector exp = vector(ZZ, x) # turn the exponents into a gap element gens_gap = self.gens() if len(exp) != len(gens_gap): raise ValueError( "input does not match the number of generators") x = gens_gap[0]**0 for i in range(len(exp)): x *= gens_gap[i]**exp[i] x = x.gap() return self.element_class(self, x, check=check)
def bezier3d(path, **options): """ Draw a 3-dimensional bezier path. Input is similar to bezier_path, but each point in the path and each control point is required to have 3 coordinates. INPUT: - ``path`` -- a list of curves, which each is a list of points. See further detail below. - ``thickness`` -- (default: 2) - ``color`` -- a string (``"red"``, ``"green"`` etc) or a tuple (r, g, b) with r, g, b numbers between 0 and 1 - ``opacity`` -- (default: 1) if less than 1 then is transparent - ``aspect_ratio`` -- (default:[1,1,1]) The path is a list of curves, and each curve is a list of points. Each point is a tuple (x,y,z). The first curve contains the endpoints as the first and last point in the list. All other curves assume a starting point given by the last entry in the preceding list, and take the last point in the list as their opposite endpoint. A curve can have 0, 1 or 2 control points listed between the endpoints. In the input example for path below, the first and second curves have 2 control points, the third has one, and the fourth has no control points:: path = [[p1, c1, c2, p2], [c3, c4, p3], [c5, p4], [p5], ...] In the case of no control points, a straight line will be drawn between the two endpoints. If one control point is supplied, then the curve at each of the endpoints will be tangent to the line from that endpoint to the control point. Similarly, in the case of two control points, at each endpoint the curve will be tangent to the line connecting that endpoint with the control point immediately after or immediately preceding it in the list. So in our example above, the curve between p1 and p2 is tangent to the line through p1 and c1 at p1, and tangent to the line through p2 and c2 at p2. Similarly, the curve between p2 and p3 is tangent to line(p2,c3) at p2 and tangent to line(p3,c4) at p3. Curve(p3,p4) is tangent to line(p3,c5) at p3 and tangent to line(p4,c5) at p4. Curve(p4,p5) is a straight line. EXAMPLES:: sage: path = [[(0,0,0),(.5,.1,.2),(.75,3,-1),(1,1,0)],[(.5,1,.2),(1,.5,0)],[(.7,.2,.5)]] sage: b = bezier3d(path, color='green') sage: b Graphics3d Object To construct a simple curve, create a list containing a single list:: sage: path = [[(0,0,0),(1,0,0),(0,1,0),(0,1,1)]] sage: curve = bezier3d(path, thickness=5, color='blue') sage: curve Graphics3d Object TESTS: Check for :trac:`31640`:: sage: p2d = [[(3,0.0),(3,0.13),(2,0.2),(2,0.3)], [(2.7,0.4),(2.6,0.5),(2.5,0.5)], [(2.3,0.5),(2.2,0.4),(2.1,0.3)]] sage: bp = bezier_path(p2d) sage: bp.plot3d() Graphics3d Object sage: p3d = p3d = [[(3,0,0),(3,0.1,0),(2.9,0.2,0),(2.8,0.3,0)], [(2.7,0.4,0),(2,0.5,0),(2.5,0.5,0)], [(2.3,0.5,0),(2.2,0.4,0),(2.1,0.3,0)]] sage: bezier3d(p3d) Graphics3d Object """ from . import parametric_plot3d as P3D from sage.modules.free_module_element import vector from sage.symbolic.ring import SR p0 = vector(path[0][-1]) t = SR.var('t') if len(path[0]) > 2: B = (1 - t)**3 * vector(path[0][0]) + 3 * t * (1 - t)**2 * vector( path[0][1]) + 3 * t**2 * (1 - t) * vector(path[0][-2]) + t**3 * p0 G = P3D.parametric_plot3d(list(B), (0, 1), color=options['color'], aspect_ratio=options['aspect_ratio'], thickness=options['thickness'], opacity=options['opacity']) else: G = line3d([path[0][0], p0], color=options['color'], thickness=options['thickness'], opacity=options['opacity']) for curve in path[1:]: if len(curve) > 1: p1 = vector(curve[0]) p2 = vector(curve[-2]) p3 = vector(curve[-1]) B = (1 - t)**3 * p0 + 3 * t * (1 - t)**2 * p1 + 3 * t**2 * ( 1 - t) * p2 + t**3 * p3 G += P3D.parametric_plot3d(list(B), (0, 1), color=options['color'], aspect_ratio=options['aspect_ratio'], thickness=options['thickness'], opacity=options['opacity']) else: G += line3d([p0, curve[0]], color=options['color'], thickness=options['thickness'], opacity=options['opacity']) p0 = vector(curve[-1]) return G
def series_sum_regular(Intervals, dop, bwrec, ini, pt, stop, stride): r""" Sum a (logarithmic) series solution of an operator that may have a regular singular point at the origin. TESTS:: sage: from ore_algebra import * sage: from ore_algebra.analytic.naive_sum import * sage: Dops, x, Dx = DifferentialOperators() Test that we correctly compute solutions of large valuations, and that when there are several solutions with very different valuations, we can stop before reaching the largest one if the initial values there are zero. (Unfortunately, the bounds in this kind of situation are currently so pessimistic that this ability rarely helps in practice!) :: sage: #dop = (Dx-1).lclm(x*Dx-1000) sage: dop = (x^2-1000*x)*Dx^2 + (-x^2+999000)*Dx + 1000*x - 999000 sage: logger = logging.getLogger('ore_algebra.analytic.naive_sum') sage: logger.setLevel(logging.INFO) # TBI sage: dop.numerical_transition_matrix([0,1/10000000]) INFO:ore_algebra.analytic.naive_sum:... INFO:ore_algebra.analytic.naive_sum:summed 50 terms, ... [ [1.000000100000005...] [1.0000000000000000e-7000...]] [ [1.000000100000005...] [1.0000000000000000e-6990...]] sage: logger.setLevel(logging.WARNING) sage: series_sum(dop, {0: (1,), 1000: (1/1000,)}, 1, 1e-10) ([2.719281828...]) Test that we correctly take into account the errors on terms of polynomials that are not represented because they are zero:: sage: dop = x*Dx^2 + Dx + x sage: ini = LogSeriesInitialValues(0, {0: (1, 0)}) sage: maj = bounds.DiffOpBound(dop, special_shifts=[(0, 1)], max_effort=0) sage: series_sum(dop, ini, QQ(2), 1e-8, stride=1, maj=maj) ([0.2238907...]) Some simple tests involving large non-integer valuations:: sage: dop = (x*Dx-1001/2).symmetric_product(Dx-1) sage: dop = dop._normalize_base_ring()[-1] sage: (exp(CBF(1/2))/RBF(2)^(1001/2)).overlaps(dop.numerical_transition_matrix([0, 1/2], 1e-10)[0,0]) True sage: (exp(CBF(2))/RBF(1/2)^(1001/2)).overlaps(dop.numerical_transition_matrix([0, 2], 1e-10)[0,0]) True sage: dop = (x*Dx+1001/2).symmetric_product(Dx-1) sage: dop = dop._normalize_base_ring()[-1] sage: (CBF(1/2)^(-1001/2)*exp(CBF(1/2))).overlaps(dop.numerical_transition_matrix([0, 1/2], 1e-10)[0,0]) True sage: (CBF(2)^(-1001/2)*exp(CBF(2))).overlaps(dop.numerical_transition_matrix([0, 2], 1e-10)[0,0]) True sage: h = CBF(1/2) sage: #dop = (Dx-1).lclm(x^2*Dx^2 - x*(2*x+1999)*Dx + (x^2 + 1999*x + 1000^2)) sage: dop = x^2*Dx^3 + (-3*x^2 - 1997*x)*Dx^2 + (3*x^2 + 3994*x + 998001)*Dx - x^2 - 1997*x - 998001 sage: mat = dop.numerical_transition_matrix([0,1/2], 1e-5) # XXX: long time with the simplified bounds on rational functions sage: mat[0,0].overlaps(exp(h)) # long time True sage: mat[0,1].overlaps(exp(h)*h^1000*log(h)) # long time True sage: mat[0,2].overlaps(exp(h)*h^1000) # long time True sage: dop = (x^3 + x^2)*Dx^3 + (-1994*x^2 - 1997*x)*Dx^2 + (994007*x + 998001)*Dx + 998001 sage: mat = dop.numerical_transition_matrix([0, 1/2], 1e-5) sage: mat[0,0].overlaps(1/(1+h)) True sage: mat[0,1].overlaps(h^1000/(1+h)*log(h)) True sage: mat[0,2].overlaps(h^1000/(1+h)) True """ jet = pt.jet(Intervals) Jets = jet.parent() ord = pt.jet_order jetpow = Jets.one() radpow = bounds.IR.one( ) # bound on abs(pt)^n in the series part (=> starts # at 1 regardless of ini.expo) log_prec = sum(len(v) for v in ini.shift.itervalues()) last_index_with_ini = max([dop.order()] + [ s for s, vals in ini.shift.iteritems() if not all(v.is_zero() for v in vals) ]) last = collections.deque( [vector(Intervals, log_prec) for _ in xrange(bwrec.order + 1)]) psum = vector(Jets, log_prec) # Every few iterations, heuristically check if we have converged and if # we still have enough precision. If it looks like the target error may # be reached, perform a rigorous check. Our stopping criterion currently # (1) only works at “generic” indices, and (2) assumes that the initial # values at exceptional indices larger than n are zero, so we also # ensure that we are in this case. (Both assumptions could be lifted, # (1) by using a slightly more complicated formula for the tail bound, # and (2) if we had code to compute lower bounds on coefficients of # series expansions of majorants.) tail_bound = bounds.IR(infinity) bit_prec = Intervals.precision() ini_are_accurate = 2 * min(pt.accuracy(), ini.accuracy()) > bit_prec # TODO: improve the automatic increase of precision for large x^λ: # we currently check the series part only (which would sort of make # sense in a relative error setting) val = [None] # XXX could be more elegant :-) def get_bound(maj): tb = maj.bound(pt.rad, rows=pt.jet_order) my_psum = vector(Jets, [[t[i].add_error(tb.abs()) for i in range(ord)] for t in psum]) # XXX decouple this from the summation => less redundant computation of # local monodromy matrices val[0] = log_series_value(Jets, ord, ini.expo, my_psum, jet[0], branch=pt.branch) return max([RBF.zero()] + [_get_error(c) for c in val[0]]) def get_residuals(): return [stop.maj.normalized_residual(n, list(last)[1:], bwrec_nplus)] def get_value(): my_psum = vector(Jets, [[t[i] for i in range(ord)] for t in psum]) my_val = log_series_value(Jets, ord, ini.expo, my_psum, jet[0], branch=pt.branch) return my_val precomp_len = max(1, bwrec.order) # hack for recurrences of order zero bwrec_nplus = collections.deque((bwrec.eval_series(Intervals, i, log_prec) for i in xrange(precomp_len)), maxlen=precomp_len) for n in itertools.count(): last.rotate(1) logger.log(logging.DEBUG - 2, "n = %s, [c(n), c(n-1), ...] = %s", n, list(last)) logger.log(logging.DEBUG - 1, "n = %s, sum = %s", n, psum) mult = len(ini.shift.get(n, ())) if n % stride == 0: radpowest = abs(jetpow[0]) est = sum(abs(a) for log_jet in last for a in log_jet) * radpowest sing = (n <= last_index_with_ini) or (mult > 0) done, tail_bound = stop.check(get_bound, get_residuals, get_value, sing, n, tail_bound, est, stride) if done: break for p in xrange(log_prec - mult - 1, -1, -1): combin = sum(bwrec_nplus[0][i][j] * last[i][p + j] for j in xrange(log_prec - p) for i in xrange(bwrec.order, 0, -1)) combin += sum(bwrec_nplus[0][0][j] * last[0][p + j] for j in xrange(mult + 1, log_prec - p)) last[0][mult + p] = -~bwrec_nplus[0][0][mult] * combin for p in xrange(mult - 1, -1, -1): last[0][p] = ini.shift[n][p] psum += last[0] * jetpow jetpow = jetpow._mul_trunc_(jet, ord) radpow *= pt.rad bwrec_nplus.append( bwrec.eval_series(Intervals, n + precomp_len, log_prec)) logger.info("summed %d terms, global tail bound = %s (est = %s)", n, tail_bound, bounds.IR(est)) result = vector(val[0][i] for i in xrange(ord)) return result
def find_primitive_p_divisible_vector__next(self, p, v=None): """ Finds the next `p`-primitive vector (up to scaling) in `L/pL` whose value is `p`-divisible, where the last vector returned was `v`. For an initial call, no `v` needs to be passed. Returns vectors whose last non-zero entry is normalized to 0 or 1 (so no lines are counted repeatedly). The ordering is by increasing the first non-normalized entry. If we have tested all (lines of) vectors, then return None. OUTPUT: vector or None EXAMPLES:: sage: Q = QuadraticForm(ZZ, 2, [10,1,4]) sage: v = Q.find_primitive_p_divisible_vector__next(5); v (1, 1) sage: v = Q.find_primitive_p_divisible_vector__next(5, v); v (1, 0) sage: v = Q.find_primitive_p_divisible_vector__next(5, v); v """ ## Initialize n = self.dim() if v is None: w = vector([ZZ(0) for i in range(n - 1)] + [ZZ(1)]) else: w = deepcopy(v) ## Handle n = 1 separately. if n <= 1: raise RuntimeError("Sorry -- Not implemented yet!") ## Look for the last non-zero entry (which must be 1) nz = n - 1 while w[nz] == 0: nz += -1 ## Test that the last non-zero entry is 1 (to detect tampering). if w[nz] != 1: print( "Warning: The input vector to QuadraticForm.find_primitive_p_divisible_vector__next() is not normalized properly." ) ## Look for the next vector, until w == 0 while True: ## Look for the first non-maximal (non-normalized) entry ind = 0 while (ind < nz) and (w[ind] == p - 1): ind += 1 ## Increment if (ind < nz): w[ind] += 1 for j in range(ind): w[j] = 0 else: for j in range(ind + 1): ## Clear all entries w[j] = 0 if nz != 0: ## Move the non-zero normalized index over by one, or return the zero vector w[nz - 1] = 1 nz += -1 ## Test for zero vector if w == 0: return None ## Test for p-divisibility if (self(w) % p == 0): return w
def is_convex(self): """ sage: from cutgeneratingfunctionology.spam.polyhedral_complex import PolyhedralComplex sage: from sage.geometry.polyhedron.constructor import Polyhedron sage: pc = PolyhedralComplex([Polyhedron(base_ring=QQ, vertices=[[1,0],[0,1]], ....: rays=[[1,0],[0,1]])]) sage: pc.is_convex() True sage: pc = PolyhedralComplex([Polyhedron(base_ring=QQ, vertices=[[1,0,0],[0,1,0]], ....: rays=[[1,0,0],[0,1,0]])]) sage: pc.is_convex() True sage: pc = PolyhedralComplex([Polyhedron(base_ring=QQ, vertices=[[-1,0],[1,0]], ....: lines=[[0,1]])]) sage: pc.is_convex() True sage: pc = PolyhedralComplex([Polyhedron(base_ring=QQ, vertices=[[1,0],[0,1]], ....: rays=[[1,0],[0,1]]),Polyhedron(base_ring=QQ, vertices=[[1,0],[0,-1]], ....: rays=[[1,0],[0,-1]])]) sage: pc.is_convex() False sage: pc = PolyhedralComplex([Polyhedron(base_ring=QQ, vertices=[[0,0]], ....: rays=[[1,0],[-1,1]]),Polyhedron(base_ring=QQ, vertices=[[0,0]], ....: rays=[[1,0],[-1,-1]])]) sage: pc.is_convex() False sage: pc = PolyhedralComplex([Polyhedron(base_ring=QQ, vertices=[[0,0,0]], ....: rays=[[1,0,0],[-1,1,0]]),Polyhedron(base_ring=QQ, vertices=[[0,0,0]], ....: rays=[[1,0,0],[-1,-1,0]])]) sage: pc.is_convex() False sage: pc = PolyhedralComplex([Polyhedron(base_ring=QQ, vertices=[[0,0,0]],rays=[[1,0,0],[0,1,0],[0,0,-1]]), ....: Polyhedron(base_ring=QQ, vertices=[[0,0,0]],rays=[[1,0,0],[0,-1,0],[0,0,-1]]), ....: Polyhedron(base_ring=QQ, vertices=[[0,0,0]],rays=[[1,0,0],[0,-1,0],[0,0,1]]), ....: Polyhedron(base_ring=QQ, vertices=[[0,0,0]],rays=[[-1,0,0],[0,-1,0],[0,0,-1]]), ....: Polyhedron(base_ring=QQ, vertices=[[0,0,0]],rays=[[-1,0,0],[0,-1,0],[0,0,1]]), ....: Polyhedron(base_ring=QQ, vertices=[[0,0,0]],rays=[[-1,0,0],[0,1,0],[0,0,-1]]), ....: Polyhedron(base_ring=QQ, vertices=[[0,0,0]],rays=[[-1,0,0],[0,1,0],[0,0,1]])]) sage: pc.is_convex() False """ if hasattr(self, '_is_convex'): # FIXME: bad! _is_convex can not be changed later. return self._is_convex if not self.is_pure(): self._is_convex = False return False if not self.is_full_dimensional(): from sage.modules.free_module import span face = self._maximal_cells[self._dim][0] affine_space = span(face.equations_list(), face.base_ring()) for face in self._maximal_cells[self._dim][1::]: if span(face.equations_list(), face.base_ring()) != affine_space: self._is_convex = False return False # # If they lie in different subspaces, can't be convex. When they all lie in the same subspace, then you orient the boundary halfspaces toward a strict convex combination of the vertices. Then you check whether all vertices are contained. After you made sure that the affine hulls of the cells are the same, it does not matter that is not full dimensional. boundaries = self.boundary_cells() vertices = set([]) rays = set([]) lines = set([]) # lines are useless, because they are in the affine space of each boundary cell. for cell in boundaries: # is that enough, or need vertices of all cells? I think that is enough. for v in cell.vertices_list(): vv = vector(v) vv.set_immutable() vertices.add(vv) for cell in self._maximal_cells[self._dim]: for r in cell.rays_list(): rr = vector(r) rr.set_immutable() rays.add(rr) for l in cell.lines_list(): ll = vector(l) ll.set_immutable() lines.add(ll) center = sum(vertices) / len(vertices) for cell in boundaries: for equation in cell.equations_list(): # if not full-dim, cell has more than one equaiton. coeff = vector(equation[1::]) const = equation[0] if const + coeff * center == 0: sign = 0 elif const + coeff * center > 0: sign = 1 for v in vertices: if const + coeff * v < 0: self._is_convex = False return False elif const + coeff * center < 0: sign = -1 for v in vertices: if const + coeff * v > 0: self._is_convex = False return False for r in rays: if sign == 0: sign = coeff * r else: if sign * (coeff * r) < 0: self._is_convex = False return False self._is_convex = True self._polyhedron = Polyhedron(vertices=vertices,rays=rays,lines=lines) return True
def find_p_neighbor_from_vec(self, p, v): """ Finds the `p`-neighbor of this quadratic form associated to a given vector `v` satisfying: #. `Q(v) = 0 \pmod p` #. `v` is a non-singular point of the conic `Q(v) = 0 \pmod p`. Reference: Gonzalo Tornaria's Thesis, Thrm 3.5, p34. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ,[1,1,1,1]) sage: v = vector([0,2,1,1]) sage: X = Q.find_p_neighbor_from_vec(3,v); X Quadratic form in 4 variables over Integer Ring with coefficients: [ 3 10 0 -4 ] [ * 9 0 -6 ] [ * * 1 0 ] [ * * * 2 ] """ R = self.base_ring() n = self.dim() B2 = self.matrix() ## Find a (dual) vector w with B(v,w) != 0 (mod p) v_dual = B2 * vector( v) ## We want the dot product with this to not be divisible by 2*p. y_ind = 0 while ((y_ind < n) and (v_dual[y_ind] % p) == 0): ## Check the dot product for the std basis vectors! y_ind += 1 if y_ind == n: raise RuntimeError( "Oops! One of the standard basis vectors should have worked.") w = vector([R(i == y_ind) for i in range(n)]) vw_prod = (v * self.matrix()).dot_product(w) ## DIAGNOSTIC #if vw_prod == 0: # print "v = ", v # print "v_dual = ", v_dual # print "v_dual[y_ind] = ", v_dual[y_ind] # print "(v_dual[y_ind] % p) = ", (v_dual[y_ind] % p) # print "w = ", w # print "p = ", p # print "vw_prod = ", vw_prod # raise RuntimeError, "ERROR: Why is vw_prod = 0?" ## DIAGNOSTIC #print "v = ", v #print "w = ", w #print "vw_prod = ", vw_prod ## Lift the vector v to a vector v1 s.t. Q(v1) = 0 (mod p^2) s = self(v) if (s % p**2 != 0): al = (-s / (p * vw_prod)) % p v1 = v + p * al * w v1w_prod = (v1 * self.matrix()).dot_product(w) else: v1 = v v1w_prod = vw_prod ## DIAGNOSTIC #if (s % p**2 != 0): # print "al = ", al #print "v1 = ", v1 #print "v1w_prod = ", v1w_prod ## Construct a special p-divisible basis to use for the p-neighbor switch good_basis = extend_to_primitive([v1, w]) for i in range(2, n): ith_prod = (good_basis[i] * self.matrix()).dot_product(v) c = (ith_prod / v1w_prod) % p good_basis[i] = good_basis[ i] - c * w ## Ensures that this extension has <v_i, v> = 0 (mod p) ## DIAGNOSTIC #print "original good_basis = ", good_basis ## Perform the p-neighbor switch good_basis[0] = vector([x / p for x in good_basis[0]]) ## Divide v1 by p good_basis[1] = good_basis[1] * p ## Multiply w by p ## Return the associated quadratic form M = matrix(good_basis) new_Q = deepcopy( self) ## Note: This avoids a circular import of QuadraticForm! new_Q.__init__(R, M * self.matrix() * M.transpose()) return new_Q return QuadraticForm(R, M * self.matrix() * M.transpose())
def semi_norm_cone(M, cone, p=2, verbose=False): r""" Return the semi norm on the hyperplane orthogonal to v where v lives in the cone. EXAMPLES: For Arnoux-Rauzy, only the 1-norm works:: sage: from slabbe.matrix_cocycle import semi_norm_cone sage: A1 = matrix(3, [1,1,1, 0,1,0, 0,0,1]) sage: cone = A1 sage: semi_norm_cone(A1.transpose(), cone, p=1) # tolerance 0.00001 0.9999999999999998 sage: semi_norm_cone(A1.transpose(), cone, p=oo) # tolerance 0.0001 1.9999757223144654 sage: semi_norm_cone(A1.transpose(), cone, p=2) # tolerance 0.00001 1.3065629648763757 For Poincaré, all norms work:: sage: P21 = matrix(3, [1,1,1, 0,1,1, 0,0,1]) sage: H21 = matrix(3, [1,0,0, 0,1,0, 1,0,1]) sage: cone = P21 * H21 sage: semi_norm_cone(P21.transpose(), cone, p=1) # tolerance 0.00001 0.9999957276014074 sage: semi_norm_cone(P21.transpose(), cone, p=oo) # tolerance 0.00001 1.0 sage: semi_norm_cone(P21.transpose(), cone, p=2) # tolerance 0.00001 0.9999999999670175 For Poincaré on the whole cone, it works for some norms:: sage: P21 = matrix(3, [1,1,1, 0,1,1, 0,0,1]) sage: cone = P21 sage: semi_norm_cone(P21.transpose(), cone, p=1) # tolerance 0.0001 1.9999675644077723 sage: semi_norm_cone(P21.transpose(), cone, p=2) # tolerance 0.00001 1.6180339887021953 sage: semi_norm_cone(P21.transpose(), cone, p=oo) # tolerance 0.00001 1.0 For a product, all norms work:: sage: A1 = matrix(3, [1,1,1, 0,1,0, 0,0,1]) sage: P21 = matrix(3, [1,1,1, 0,1,1, 0,0,1]) sage: H21 = matrix(3, [1,0,0, 0,1,0, 1,0,1]) sage: M = A1 * P21 sage: cone = A1 * P21 * H21 sage: semi_norm_cone(M.transpose(), cone, p=1) # tolerance 0.00001 0.999993244882415 sage: semi_norm_cone(M.transpose(), cone, p=oo) # tolerance 0.00001 0.9999935206958908 sage: semi_norm_cone(M.transpose(), cone, p=2) # tolerance 0.00001 0.7529377601317161 """ from sage.modules.free_module_element import vector from sage.numerical.optimize import minimize_constrained a,b,c = cone.columns() ab = vector(matrix((a,b)).right_kernel_matrix().row(0)) ac = vector(matrix((a,c)).right_kernel_matrix().row(0)) bc = vector(matrix((b,c)).right_kernel_matrix().row(0)) middle = a+b+c cons = [] if ab * middle < 0: cons.append(lambda z: -ab * vector(z)) else: cons.append(lambda z: ab * vector(z)) if ac * middle < 0: cons.append(lambda z: -ac * vector(z)) else: cons.append(lambda z: ac * vector(z)) if bc * middle < 0: cons.append(lambda z: -bc * vector(z)) else: cons.append(lambda z: bc * vector(z)) if not all(con(middle) > 0 for con in cons): raise ValueError("the middle should be in the cone") func = lambda v : - semi_norm_v(M,vector(v),p) x0 = middle rep = minimize_constrained(func, cons, x0) if not all((con(rep) >= 0 or abs(con(rep)) < 1e-7) for con in cons): raise ValueError("the answer (={}) should be in the cone".format(rep)) if not all(r >= 0 or abs(r) < 1e-7 for r in rep): raise ValueError("the answer (={}) should be positive".format(rep)) if verbose: print "optimal found at ", rep / rep.norm(p) return -func(rep)
def jacobian(*X): r""" Compute the Jacobian (Rankin--Cohen--Ibukiyama) operator. INPUT: - ``X`` -- a list [F_1, ..., F_N] of N orthogonal modular forms for the same Gram matrix, where N = 3 + (number of self's gram matrix rows) OUTPUT: OrthogonalModularForm. (If F_1, ..., F_N have weights k_1, ..., k_N then the result has weight k_1 + ... + k_N + N - 1.) EXAMPLES:: sage: from weilrep import * sage: jacobian([ParamodularForms(1).eisenstein_series(k, 7) for k in [4, 6, 10, 12]]) / (-589927441461779261030400000/2354734631251) #funny multiple (r^-1 - r)*q^3*s^2 + (-r^-1 + r)*q^2*s^3 + (-r^-3 - 69*r^-1 + 69*r + r^3)*q^4*s^2 + (r^-3 + 69*r^-1 - 69*r - r^3)*q^2*s^4 + O(q, s)^7 """ N = len(X) if N == 1: X = X[0] N = len(X) Xref = X[0] nvars = Xref.nvars() f = Xref.true_fourier_expansion() t, = f.parent().gens() rb_x = f.base_ring() x, = rb_x.gens() r_list = rb_x.base_ring().gens() if N != nvars + 1: raise ValueError('The Jacobian requires %d modular forms.' % (nvars + 1)) k = N - 1 v = vector([0] * nvars) r_deriv = [[] for _ in r_list] t_deriv = [] x_deriv = [] u = [] S = Xref.gram_matrix() new_scale = lcm(x.scale() for x in X) for y in X: if y.gram_matrix() != S: raise ValueError('These forms do not have the same Gram matrix.') f = y.rescale(new_scale // y.scale()).true_fourier_expansion() t_deriv.append(t * f.derivative()) if nvars > 1: x_deriv.append(f.map_coefficients(lambda a: x * a.derivative())) if nvars > 2: for i, r in enumerate(r_list): r_deriv[i].append( f.map_coefficients(lambda a: rb_x([ r * y.derivative(r) for y in list(a) ]) * (x**(a.polynomial_construction()[1])))) y_k = y.weight() k += y_k v += y.weyl_vector() u.append(y_k * f) L = [u, t_deriv] if nvars > 1: L.append(x_deriv) if nvars > 2: L.extend(r_deriv) return OrthogonalModularForm( k, Xref.weilrep(), matrix(L).determinant(), scale=new_scale, weylvec=v, qexp_representation=Xref.qexp_representation())
def balance_sample(s, q=None): r""" Given ``(a,c) = s`` return a tuple ``(a',c')`` where ``a'`` is a integer vector with entries between -q//2 and q//2 and ``c`` is also within these bounds. If ``q`` is given ``(a,c) = s`` may live in the integers. If ``q`` is not given, then ``(a,c)`` are assumed to live in `\Zmod{q}`. INPUT: - ``s`` - sample of the form (a,c) where a is a vector and c is a scalar - ``q`` - modulus (default: ``None``) EXAMPLE:: sage: map(balance_sample, samples(10, 5, Regev)) [((-9, -4, -4, 4, -4), 6), ((-3, -10, 8, -3, -1), -10), ((-6, -12, -3, -2, -6), -6), ... ((-1, -8, -11, 13, 4), -6), ((10, 11, -3, -13, 0), 6), ((6, -1, 2, -11, 14), 2)] sage: D = DiscreteGaussianPolynomialSamplerRejection(8, 5) sage: rlwe = RingLWE(20, 257, D) sage: map(balance_sample, samples(10, 8, rlwe)) [((5, -55, -31, -90, 6, 100, -46, -107), (6, -64, -40, 117, 27, 54, -98, -56)), ((109, -106, 28, 77, -14, -109, 115, 34), (82, 17, -89, 62, 1, -77, 128, 64)), ... ((-32, 51, -110, -106, 35, -82, 14, -113), (126, -120, 126, 119, 101, 3, -122, -75))] .. note:: This function is useful to convert between Sage's standard representation of elements in `\Zmod{q}` as integers between 0 and q-1 and the usual representation of such elements in lattice cryptography as integers between -q//2 and q//2. """ a, c = s try: c[0] scalar = False except TypeError: c = vector(c.parent(), [c]) scalar = True if q is None: q = parent(c[0]).order() a = a.change_ring(ZZ) c = c.change_ring(ZZ) else: K = IntegerModRing(q) a = a.change_ring(K).change_ring(ZZ) c = c.change_ring(K).change_ring(ZZ) q2 = q // 2 if scalar: return vector(ZZ, len(a), [e if e <= q2 else e - q for e in a]), c[0] if c[0] <= q2 else c[0] - q else: return vector(ZZ, len(a), [e if e <= q2 else e - q for e in a]), vector( ZZ, len(c), [e if e <= q2 else e - q for e in c])
def elem_vector(i, dim): vec = [0] * dim vec[i] = 1 return vector(vec)
def linear_program(c, G, h, A=None, b=None, solver=None): """ Solves the dual linear programs: - Minimize `c'x` subject to `Gx + s = h`, `Ax = b`, and `s \geq 0` where `'` denotes transpose. - Maximize `-h'z - b'y` subject to `G'z + A'y + c = 0` and `z \geq 0`. INPUT: - ``c`` -- a vector - ``G`` -- a matrix - ``h`` -- a vector - ``A`` -- a matrix - ``b`` --- a vector - ``solver`` (optional) --- solver to use. If None, the cvxopt's lp-solver is used. If it is 'glpk', then glpk's solver is used. These can be over any field that can be turned into a floating point number. OUTPUT: A dictionary ``sol`` with keys ``x``, ``s``, ``y``, ``z`` corresponding to the variables above: - ``sol['x']`` -- the solution to the linear program - ``sol['s']`` -- the slack variables for the solution - ``sol['z']``, ``sol['y']`` -- solutions to the dual program EXAMPLES: First, we minimize `-4x_1 - 5x_2` subject to `2x_1 + x_2 \leq 3`, `x_1 + 2x_2 \leq 3`, `x_1 \geq 0`, and `x_2 \geq 0`:: sage: c=vector(RDF,[-4,-5]) sage: G=matrix(RDF,[[2,1],[1,2],[-1,0],[0,-1]]) sage: h=vector(RDF,[3,3,0,0]) sage: sol=linear_program(c,G,h) sage: sol['x'] (0.999..., 1.000...) Next, we maximize `x+y-50` subject to `50x + 24y \leq 2400`, `30x + 33y \leq 2100`, `x \geq 45`, and `y \geq 5`:: sage: v=vector([-1.0,-1.0,-1.0]) sage: m=matrix([[50.0,24.0,0.0],[30.0,33.0,0.0],[-1.0,0.0,0.0],[0.0,-1.0,0.0],[0.0,0.0,1.0],[0.0,0.0,-1.0]]) sage: h=vector([2400.0,2100.0,-45.0,-5.0,1.0,-1.0]) sage: sol=linear_program(v,m,h) sage: sol['x'] (45.000000..., 6.2499999...3, 1.00000000...) sage: sol=linear_program(v,m,h,solver='glpk') GLPK Simplex Optimizer... OPTIMAL SOLUTION FOUND sage: sol['x'] (45.0..., 6.25, 1.0...) """ from cvxopt.base import matrix as m from cvxopt import solvers solvers.options['show_progress'] = False if solver == 'glpk': from cvxopt import glpk glpk.options['LPX_K_MSGLEV'] = 0 c_ = m(c.base_extend(RDF).numpy()) G_ = m(G.base_extend(RDF).numpy()) h_ = m(h.base_extend(RDF).numpy()) if A != None and b != None: A_ = m(A.base_extend(RDF).numpy()) b_ = m(b.base_extend(RDF).numpy()) sol = solvers.lp(c_, G_, h_, A_, b_, solver=solver) else: sol = solvers.lp(c_, G_, h_, solver=solver) status = sol['status'] if status != 'optimal': return { 'primal objective': None, 'x': None, 's': None, 'y': None, 'z': None, 'status': status } x = vector(RDF, list(sol['x'])) s = vector(RDF, list(sol['s'])) y = vector(RDF, list(sol['y'])) z = vector(RDF, list(sol['z'])) return { 'primal objective': sol['primal objective'], 'x': x, 's': s, 'y': y, 'z': z, 'status': status }
def minimize(func, x0, gradient=None, hessian=None, algorithm="default", **args): r""" This function is an interface to a variety of algorithms for computing the minimum of a function of several variables. INPUT: - ``func`` -- Either a symbolic function or a Python function whose argument is a tuple with `n` components - ``x0`` -- Initial point for finding minimum. - ``gradient`` -- Optional gradient function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the partial derivatives at that point. - ``hessian`` -- Optional hessian function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the second partial derivatives of the function. - ``algorithm`` -- String specifying algorithm to use. Options are ``'default'`` (for Python functions, the simplex method is the default) (for symbolic functions bfgs is the default): - ``'simplex'`` - ``'powell'`` - ``'bfgs'`` -- (Broyden-Fletcher-Goldfarb-Shanno) requires ``gradient`` - ``'cg'`` -- (conjugate-gradient) requires gradient - ``'ncg'`` -- (newton-conjugate gradient) requires gradient and hessian EXAMPLES:: sage: vars=var('x y z') sage: f=100*(y-x^2)^2+(1-x)^2+100*(z-y^2)^2+(1-y)^2 sage: minimize(f,[.1,.3,.4],disp=0) (1.00..., 1.00..., 1.00...) sage: minimize(f,[.1,.3,.4],algorithm="ncg",disp=0) (0.9999999..., 0.999999..., 0.999999...) Same example with just Python functions:: sage: def rosen(x): # The Rosenbrock function ... return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: minimize(rosen,[.1,.3,.4],disp=0) (1.00..., 1.00..., 1.00...) Same example with a pure Python function and a Python function to compute the gradient:: sage: def rosen(x): # The Rosenbrock function ... return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: import numpy sage: from numpy import zeros sage: def rosen_der(x): ... xm = x[1r:-1r] ... xm_m1 = x[:-2r] ... xm_p1 = x[2r:] ... der = zeros(x.shape,dtype=float) ... der[1r:-1r] = 200r*(xm-xm_m1**2r) - 400r*(xm_p1 - xm**2r)*xm - 2r*(1r-xm) ... der[0] = -400r*x[0r]*(x[1r]-x[0r]**2r) - 2r*(1r-x[0]) ... der[-1] = 200r*(x[-1r]-x[-2r]**2r) ... return der sage: minimize(rosen,[.1,.3,.4],gradient=rosen_der,algorithm="bfgs",disp=0) (1.00..., 1.00..., 1.00...) """ from sage.symbolic.expression import Expression from sage.ext.fast_eval import fast_callable import scipy from scipy import optimize if isinstance(func, Expression): var_list = func.variables() var_names = map(str, var_list) fast_f = fast_callable(func, vars=var_names, domain=float) f = lambda p: fast_f(*p) gradient_list = func.gradient() fast_gradient_functions = [ fast_callable(gradient_list[i], vars=var_names, domain=float) for i in xrange(len(gradient_list)) ] gradient = lambda p: scipy.array( [a(*p) for a in fast_gradient_functions]) else: f = func if algorithm == "default": if gradient == None: min = optimize.fmin(f, map(float, x0), **args) else: min = optimize.fmin_bfgs(f, map(float, x0), fprime=gradient, **args) else: if algorithm == "simplex": min = optimize.fmin(f, map(float, x0), **args) elif algorithm == "bfgs": min = optimize.fmin_bfgs(f, map(float, x0), fprime=gradient, **args) elif algorithm == "cg": min = optimize.fmin_cg(f, map(float, x0), fprime=gradient, **args) elif algorithm == "powell": min = optimize.fmin_powell(f, map(float, x0), **args) elif algorithm == "ncg": if isinstance(func, Expression): hess = func.hessian() hess_fast = [[ fast_callable(a, vars=var_names, domain=float) for a in row ] for row in hess] hessian = lambda p: [[a(*p) for a in row] for row in hess_fast] hessian_p = lambda p, v: scipy.dot(scipy.array(hessian(p)), v) min = optimize.fmin_ncg(f, map(float, x0), fprime=gradient, fhess=hessian, fhess_p=hessian_p, **args) return vector(RDF, min)