def plot(self, max_points=2500, **args): r""" Create a visualization of this `p`-adic ring as a fractal similar to a generalization of the Sierpi\'nski triangle. The resulting image attempts to capture the algebraic and topological characteristics of `\mathbb{Z}_p`. INPUT: - ``max_points`` -- the maximum number or points to plot, which controls the depth of recursion (default 2500) - ``**args`` -- color, size, etc. that are passed to the underlying point graphics objects REFERENCES: - Cuoco, A. ''Visualizing the `p`-adic Integers'', The American Mathematical Monthly, Vol. 98, No. 4 (Apr., 1991), pp. 355-364 EXAMPLES:: sage: Zp(3).plot() Graphics object consisting of 1 graphics primitive sage: Zp(5).plot(max_points=625) Graphics object consisting of 1 graphics primitive sage: Zp(23).plot(rgbcolor=(1,0,0)) Graphics object consisting of 1 graphics primitive """ if 'pointsize' not in args: args['pointsize'] = 1 from sage.misc.mrange import cartesian_product_iterator from sage.rings.real_double import RDF from sage.plot.all import points, circle, Graphics p = self.prime() phi = 2*RDF.pi()/p V = RDF**2 vs = [V([(phi*t).sin(), (phi*t).cos()]) for t in range(p)] all = [] depth = max(RDF(max_points).log(p).floor(), 1) scale = min(RDF(1.5/p), 1/RDF(3)) pts = [vs]*depth if depth == 1 and 23 < p < max_points: extras = int(max_points/p) if p/extras > 5: pts = [vs]*depth + [vs[::extras]] for digits in cartesian_product_iterator(pts): p = sum([v * scale**n for n, v in enumerate(digits)]) all.append(tuple(p)) g = points(all, **args) # Set default plotting options g.axes(False) g.set_aspect_ratio(1) return g
def plot(self, max_points=2500, **args): r""" Create a visualization of this `p`-adic ring as a fractal similar to a generalization of the Sierpi\'nski triangle. The resulting image attempts to capture the algebraic and topological characteristics of `\mathbb{Z}_p`. INPUT: - ``max_points`` -- the maximum number or points to plot, which controls the depth of recursion (default 2500) - ``**args`` -- color, size, etc. that are passed to the underlying point graphics objects REFERENCES: - Cuoco, A. ''Visualizing the `p`-adic Integers'', The American Mathematical Monthly, Vol. 98, No. 4 (Apr., 1991), pp. 355-364 EXAMPLES:: sage: Zp(3).plot() Graphics object consisting of 1 graphics primitive sage: Zp(5).plot(max_points=625) Graphics object consisting of 1 graphics primitive sage: Zp(23).plot(rgbcolor=(1,0,0)) Graphics object consisting of 1 graphics primitive """ if 'pointsize' not in args: args['pointsize'] = 1 from sage.misc.mrange import cartesian_product_iterator from sage.rings.real_double import RDF from sage.plot.all import points, circle, Graphics p = self.prime() phi = 2 * RDF.pi() / p V = RDF**2 vs = [V([(phi * t).sin(), (phi * t).cos()]) for t in range(p)] all = [] depth = max(RDF(max_points).log(p).floor(), 1) scale = min(RDF(1.5 / p), 1 / RDF(3)) pts = [vs] * depth if depth == 1 and 23 < p < max_points: extras = int(max_points / p) if p / extras > 5: pts = [vs] * depth + [vs[::extras]] for digits in cartesian_product_iterator(pts): p = sum([v * scale**n for n, v in enumerate(digits)]) all.append(tuple(p)) g = points(all, **args) # Set default plotting options g.axes(False) g.set_aspect_ratio(1) return g
def tensor_term_iterator(self): """Returns a coefficient, a list of S partitions, and a list of Q partitions which parametrize one term in the chern class of the tensor product of the bundles specified by the input data. INPUT: self OUTPUT: yields a ChernClassMonomial object """ if self.blocks_computed == False: self.compute_block_sizes() FL_part = [] n = self.num_nodes for i in range(n): FL_part.append(flagged_partition_iterator(self.l[i],self.r[i])) CPI = cartesian_product_iterator( [ list(a) for a in FL_part ] ) for c in CPI: M = ChernClassMonomial() for i in range(n): (lam,mu) = c[i] M.append( ChernClass( dcoeff(lam,mu,self.l[i]), 's', i+1, mu ) ) M.append( ChernClass( 1, 'q', i+1, conj( dual(lam,self.l[i],self.r[i]) ))) yield M
def square_roots_of_one(self): """ Return all square roots of 1 in self, i.e., all solutions to `x^2 - 1 = 0`. OUTPUT: The square roots of 1 in ``self`` as a tuple. EXAMPLES:: sage: R = Integers(2^10) sage: [x for x in R if x^2 == 1] [1, 511, 513, 1023] sage: R.square_roots_of_one() (1, 511, 513, 1023) :: sage: v = Integers(9*5).square_roots_of_one(); v (1, 19, 26, 44) sage: [x^2 for x in v] [1, 1, 1, 1] sage: v = Integers(9*5*8).square_roots_of_one(); v (1, 19, 71, 89, 91, 109, 161, 179, 181, 199, 251, 269, 271, 289, 341, 359) sage: [x^2 for x in v] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ try: return self.__square_roots_of_one except AttributeError: pass n = self.__order if n.is_prime_power(): if n % 2 == 0: # power of 2 if n == 2: v = [self(1)] elif n == 4: v = [self(1), self(3)] else: # n >= 8 half_ord = n // 2 v = [ self(1), self(-1), self(half_ord - 1), self(half_ord + 1) ] else: v = [self(1), self(-1)] else: # Reduce to the prime power case. F = self.factored_order() vmod = [] moduli = [] for p, e in F: k = p**e R = IntegerModRing(p**e) w = [self(x) for x in R.square_roots_of_one()] vmod.append(w) moduli.append(k) # Now combine in all possible ways using the CRT basis = CRT_basis(moduli) from sage.misc.mrange import cartesian_product_iterator v = [] for x in cartesian_product_iterator(vmod): # x is a specific choice of roots modulo each prime power divisor a = sum([basis[i] * x[i] for i in range(len(x))]) v.append(a) #end for #end if v.sort() v = tuple(v) self.__square_roots_of_one = v return v
def square_roots_of_one(self): """ Return all square roots of 1 in self, i.e., all solutions to `x^2 - 1 = 0`. OUTPUT: The square roots of 1 in ``self`` as a tuple. EXAMPLES:: sage: R = Integers(2^10) sage: [x for x in R if x^2 == 1] [1, 511, 513, 1023] sage: R.square_roots_of_one() (1, 511, 513, 1023) :: sage: v = Integers(9*5).square_roots_of_one(); v (1, 19, 26, 44) sage: [x^2 for x in v] [1, 1, 1, 1] sage: v = Integers(9*5*8).square_roots_of_one(); v (1, 19, 71, 89, 91, 109, 161, 179, 181, 199, 251, 269, 271, 289, 341, 359) sage: [x^2 for x in v] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ try: return self.__square_roots_of_one except AttributeError: pass n = self.__order if n.is_prime_power(): if n % 2 == 0: # power of 2 if n == 2: v = [self(1)] elif n == 4: v = [self(1), self(3)] else: # n >= 8 half_ord = n // 2 v = [self(1), self(-1), self(half_ord - 1), self(half_ord + 1)] else: v = [self(1), self(-1)] else: # Reduce to the prime power case. F = self.factored_order() vmod = [] moduli = [] for p, e in F: k = p ** e R = IntegerModRing(p ** e) w = [self(x) for x in R.square_roots_of_one()] vmod.append(w) moduli.append(k) # Now combine in all possible ways using the CRT from sage.rings.arith import CRT_basis basis = CRT_basis(moduli) from sage.misc.mrange import cartesian_product_iterator v = [] for x in cartesian_product_iterator(vmod): # x is a specific choice of roots modulo each prime power divisor a = sum([basis[i] * x[i] for i in range(len(x))]) v.append(a) # end for # end if v.sort() v = tuple(v) self.__square_roots_of_one = v return v
def subgroups(self, check=False): r""" Compute all the subgroups of this abelian group (which must be finite). TODO: This is *many orders of magnitude* slower than Magma. INPUT: - check: if True, performs the same computation in GAP and checks that the number of subgroups generated is the same. (I don't know how to convert GAP's output back into Sage, so we don't actually compare the subgroups). ALGORITHM: If the group is cyclic, the problem is easy. Otherwise, write it as a direct product A x B, where B is cyclic. Compute the subgroups of A (by recursion). Now, for every subgroup C of A x B, let G be its *projection onto* A and H its *intersection with* B. Then there is a well-defined homomorphism f: G -> B/H that sends a in G to the class mod H of b, where (a,b) is any element of C lifting a; and every subgroup C arises from a unique triple (G, H, f). EXAMPLES:: sage: AbelianGroup([2,3]).subgroups() [Multiplicative Abelian Group isomorphic to C2 x C3, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f0*f1^2], Multiplicative Abelian Group isomorphic to C2, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f0], Multiplicative Abelian Group isomorphic to C3, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f1], Trivial Abelian Group, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by []] sage: len(AbelianGroup([2,4,8]).subgroups()) 81 """ if not self.is_finite(): raise ValueError, "Group must be finite" from sage.misc.misc import verbose v = self.invariants() if len(v) <= 1: if v == [] or v[0] == 1: return [self] else: return [ self.subgroup([self.gen(0)**i]) for i in divisors(v[0])[:-1]] + [self.subgroup([])] A = AbelianGroup(v[:-1]) x = v[-1] Wsubs = A.subgroups() subgps = [] for G in Wsubs: verbose("G = subgp generated by %s" % G.gens()) verbose("invariants are:", [t.order() for t in G.gens()]) # G.invariants() doesn't work for H in divisors(x): # H = the subgroup of *index* H. its = [xrange(0, H, H/gcd(H, G.gen(i).order())) for i in xrange(len(G.gens()))] for f in cartesian_product_iterator(its): verbose("using hom from G to C_%s sending gens to %s" % (H,f)) new_sub = [] for a in xrange(len(G.gens())): new_sub.append(G.gen(a).list() + [f[a]]) if H != x: new_sub.append([0]*A.ngens() + [H]) subgps.append(self.subgroup_reduced(new_sub)) if check: from sage.interfaces.all import gap verbose("Running Gap cross-check") t = ZZ(gap.eval("Size(SubgroupsSolvableGroup(AbelianGroup(%s)))" % v)) if t != len(subgps): raise ArithmeticError, "For %s Gap finds %s subgroups, I found %s" % (v, t, len(subgps)) verbose("Gap check OK for %s: %s" % (v, t)) return subgps
def subgroups(self, check=False): r""" Compute all the subgroups of this abelian group (which must be finite). TODO: This is *many orders of magnitude* slower than Magma. INPUT: - check: if True, performs the same computation in GAP and checks that the number of subgroups generated is the same. (I don't know how to convert GAP's output back into Sage, so we don't actually compare the subgroups). ALGORITHM: If the group is cyclic, the problem is easy. Otherwise, write it as a direct product A x B, where B is cyclic. Compute the subgroups of A (by recursion). Now, for every subgroup C of A x B, let G be its *projection onto* A and H its *intersection with* B. Then there is a well-defined homomorphism f: G -> B/H that sends a in G to the class mod H of b, where (a,b) is any element of C lifting a; and every subgroup C arises from a unique triple (G, H, f). EXAMPLES:: sage: AbelianGroup([2,3]).subgroups() [Multiplicative Abelian Group isomorphic to C2 x C3, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f0*f1^2], Multiplicative Abelian Group isomorphic to C2, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f0], Multiplicative Abelian Group isomorphic to C3, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f1], Trivial Abelian Group, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by []] sage: len(AbelianGroup([2,4,8]).subgroups()) 81 """ if not self.is_finite(): raise ValueError, "Group must be finite" from sage.misc.misc import verbose v = self.invariants() if len(v) <= 1: if v == [] or v[0] == 1: return [self] else: return [ self.subgroup([self.gen(0)**i]) for i in divisors(v[0])[:-1] ] + [self.subgroup([])] A = AbelianGroup(v[:-1]) x = v[-1] Wsubs = A.subgroups() subgps = [] for G in Wsubs: verbose("G = subgp generated by %s" % G.gens()) verbose("invariants are:", [t.order() for t in G.gens()]) # G.invariants() doesn't work for H in divisors(x): # H = the subgroup of *index* H. its = [ xrange(0, H, H / gcd(H, G.gen(i).order())) for i in xrange(len(G.gens())) ] for f in cartesian_product_iterator(its): verbose("using hom from G to C_%s sending gens to %s" % (H, f)) new_sub = [] for a in xrange(len(G.gens())): new_sub.append(G.gen(a).list() + [f[a]]) if H != x: new_sub.append([0] * A.ngens() + [H]) subgps.append(self.subgroup_reduced(new_sub)) if check: from sage.interfaces.all import gap verbose("Running Gap cross-check") t = ZZ( gap.eval("Size(SubgroupsSolvableGroup(AbelianGroup(%s)))" % v)) if t != len(subgps): raise ArithmeticError, "For %s Gap finds %s subgroups, I found %s" % ( v, t, len(subgps)) verbose("Gap check OK for %s: %s" % (v, t)) return subgps