Path: blob/master/sage/groups/abelian_gps/dual_abelian_group_element.py
4105 views
"""1Elements (characters) of the dual group of a finite Abelian group.23AUTHORS:4- David Joyner (2006-07); based on abelian_group_element.py.5- David Joyner (2006-10); modifications suggested by William Stein.67EXAMPLES:8sage: F = AbelianGroup(5,[2, 3, 5, 7, 8], names="abcde")9sage: a,b,c,d,e = F.gens()10sage: Fd = DualAbelianGroup(F, names = "ABCDE")11sage: A,B,C,D,E = Fd.gens()12sage: A*B^2*D^713A*B^214sage: A(a) ## random last few digits15-1.0000000000000000 + 0.00000000000000013834419720915037*I16sage: B(b)17-0.500000000000000 + 0.866025403784439*I18sage: A(a*b) ## random last few digits19-1.0000000000000000 + 0.00000000000000013834419720915037*I20sage: (A*B*C^2*D^20*E^65).list()21[1, 1, 2, 6, 1]22sage: B^(-1)23B^22425It is important to note that lists are mutable and the26returned list is not a copy. As a result, reassignment27of an element of the list changes the object.2829sage: X = A*B*C^2*D^2*E^-630sage: X.list()31[1, 1, 2, 2, 2]32sage: X.list()[1] = -133sage: X34A*B^-1*C^2*D^2*E^23536"""3738###########################################################################39# Copyright (C) 2006 William Stein <[email protected]>40# Copyright (C) 2006 David Joyner <[email protected]>41#42# Distributed under the terms of the GNU General Public License (GPL)43# http://www.gnu.org/licenses/44###########################################################################4546import operator4748from sage.rings.integer import Integer49from sage.structure.element import MonoidElement50from sage.rings.infinity import infinity51from sage.rings.arith import *52from sage.misc.misc import prod53from sage.misc.functional import exp54from sage.rings.complex_field import is_ComplexField555657def add_strings(x, z=0):58"""59This was in sage.misc.misc but commented out. Needed to add60lists of strings in the word_problem method below.6162Return the sum of the elements of x. If x is empty,63return z.6465INPUT:66x -- iterable67z -- the "0" that will be returned if x is empty.6869OUTPUT:70object71"""72if len(x) == 0:73return z74if not isinstance(x, list):75m = x.__iter__()76y = m.next()77return reduce(operator.add, m, y)78else:79return reduce(operator.add, x[1:], x[0])808182def is_DualAbelianGroupElement(x):83return isinstance(x, DualAbelianGroupElement)8485class DualAbelianGroupElement(MonoidElement):86def __init__(self, F, X):87"""88Create an element X of the DualAbelianGroup of F.8990EXAMPLES:91sage: F = AbelianGroup(3,[7,8,9])92sage: Fd = DualAbelianGroup(F,names="ABC")93sage: A,B,C = Fd.gens()94sage: A*B^-1 in Fd95True9697"""98MonoidElement.__init__(self, F)99self.__repr = None100G = F.group()101n = G.ngens()102if isinstance(X, (int, Integer)) and X == 1:103self.__element_vector = [ 0 for i in range(n) ]104elif isinstance(X, list):105if len(X) != n:106raise IndexError, \107"Argument length (= %s) must be %s."%(len(X), n)108self.__element_vector = X109else:110raise TypeError, "Argument X (= %s) is of wrong type."%X111112def list(self):113"""114Return (a reference to) the underlying list used to represent115this element. If this is a word in an abelian group on $n$116generators, then this is a list of nonnegative integers of117length $n$.118119EXAMPLES:120sage: F = AbelianGroup(5,[2, 3, 5, 7, 8], names="abcde")121sage: a,b,c,d,e = F.gens()122sage: Ad = DualAbelianGroup(F, names = "ABCDE")123sage: A,B,C,D,E = Ad.gens()124sage: (A*B*C^2*D^20*E^65).list()125[1, 1, 2, 6, 1]126sage: X = A*B*C^2*D^2*E^-6127sage: X.list()128[1, 1, 2, 2, 2]129sage: X.list()[1] = -1130sage: X131A*B^-1*C^2*D^2*E^2132"""133return self.__element_vector134135def _repr_(self):136s = ""137A = self.parent()138n = A.ngens()139x = A.variable_names()140v = self.list()141for i in range(n):142if v[i] == 0:143continue144elif v[i] == 1:145if len(s) > 0: s += "*"146s += "%s"%x[i]147else:148if len(s) > 0: s += "*"149s += "%s^%s"%(x[i],v[i])150if len(s) == 0: s = "1"151return s152153def __mul__(self, y):154#Same as _mul_ in AbelianGroupElement155156M = self.parent()157n = M.ngens()158invs = M.invariants()159z = M(1)160xelt = self.list()161yelt = y.list()162zelt = [ xelt[i]+yelt[i] for i in range(len(xelt)) ]163if len(invs) >= n:164L = []165for i in range(len(xelt)):166if invs[i]!=0:167L.append(zelt[i]%invs[i])168if invs[i]==0:169L.append(zelt[i])170z.__element_vector = L171#print z.__element_vector172if len(invs) < n:173L1 = []174for i in range(len(invs)):175if invs[i]!=0:176L1.append(zelt[i]%invs[i])177if invs[i]==0:178L1.append(zelt[i])179L2 = [ zelt[i] for i in range(len(invs),len(xelt)) ]180z.__element_vector = L1+L2181return M(z.__element_vector)182183def __pow__(self, n):184"""185requires that len(invs) = n186"""187if not isinstance(n, (int, long, Integer)):188raise TypeError, "Argument n (= %s) must be an integer."%n189n = int(n)190M = self.parent()191N = M.ngens()192invs = M.invariants()193if n < 0:194L =[n*self.list()[i]%M.gen(i).order() for i in range(M.ngens())]195return prod([M.gen(i)**L[i] for i in range(M.ngens())])196#m = LCM(invs) ## Not very efficient version197#pw = (n)%m198#x = self**pw199#return x200elif n == 0:201return M(1)202elif n == 1:203return self204elif n == 2:205return self * self206k = n//2207return self**k * self**(n-k)208209def __cmp__(self,other):210if (self.list() != other.list()):211return -1212return 0213214def order(self):215"""216Returns the (finite) order of this element.217218EXAMPLES:219sage: F = AbelianGroup(3,[7,8,9])220sage: Fd = DualAbelianGroup(F)221sage: A,B,C = Fd.gens()222sage: (B*C).order()22372224"""225M = self.parent()226#print self, M227if self == M(1):228return Integer(1)229invs = M.invariants()230if self in M.gens():231o = invs[list(M.gens()).index(self)]232if o == 0:233return infinity234return o235L = list(self.list())236N = LCM([invs[i]/GCD(invs[i],L[i]) for i in range(len(invs)) if L[i]!=0]) ####### error here????237if N == 0:238return infinity239else:240return N241242def __call__(self,g):243"""244Computes the value of a character self on a group element245g (g must be an element of self.group())246247EXAMPLES:248sage: F = AbelianGroup(5, [2,3,5,7,8], names="abcde")249sage: a,b,c,d,e = F.gens()250sage: Fd = DualAbelianGroup(F, names="ABCDE")251sage: A,B,C,D,E = Fd.gens()252sage: A*B^2*D^7253A*B^2254sage: A(a) ## random last few digits255-1.0000000000000000 + 0.00000000000000013834419720915037*I256sage: B(b) ## random last few digits257-0.49999999999999983 + 0.86602540378443871*I258sage: A(a*b) ## random last few digits259-1.0000000000000000 + 0.00000000000000013834419720915037*I260"""261F = self.parent().base_ring()262expsX = list(self.list())263expsg = list(g.list())264invs = self.parent().invariants()265N = LCM(invs)266if is_ComplexField(F):267from sage.symbolic.constants import pi268I = F.gen()269PI = F(pi)270ans = prod([exp(2*PI*I*expsX[i]*expsg[i]/invs[i]) for i in range(len(expsX))])271return ans272ans = F(1) ## assumes F is the cyclotomic field273zeta = F.gen()274#print F,zeta275for i in range(len(expsX)):276inv_noti = N/invs[i]277ans = ans*zeta**(expsX[i]*expsg[i]*inv_noti)278return ans279280def word_problem(self, words, display=True):281"""282This is a rather hackish method and is included for completeness.283284The word problem for an instance of DualAbelianGroup as it can285for an AbelianGroup. The reason why is that word problem286for an instance of AbelianGroup simply calls GAP (which287has abelian groups implemented) and invokes "EpimorphismFromFreeGroup"288and "PreImagesRepresentative". GAP does not have duals of289abelian groups implemented. So, by using the same name290for the generators, the method below converts the problem for291the dual group to the corresponding problem on the group292itself and uses GAP to solve that.293294EXAMPLES:295sage: G = AbelianGroup(5,[3, 5, 5, 7, 8],names="abcde")296sage: Gd = DualAbelianGroup(G,names="abcde")297sage: a,b,c,d,e = Gd.gens()298sage: u = a^3*b*c*d^2*e^5299sage: v = a^2*b*c^2*d^3*e^3300sage: w = a^7*b^3*c^5*d^4*e^4301sage: x = a^3*b^2*c^2*d^3*e^5302sage: y = a^2*b^4*c^2*d^4*e^5303sage: e.word_problem([u,v,w,x,y],display=False)304[[b^2*c^2*d^3*e^5, 245]]305306The command e.word_problem([u,v,w,x,y],display=True) returns307the same list but also prints $e = (b^2*c^2*d^3*e^5)^245$.308309"""310## First convert the problem to one using AbelianGroups311import copy312from sage.groups.abelian_gps.abelian_group import AbelianGroup313from sage.interfaces.all import gap314M = self.parent()315G = M.group()316gens = M.variable_names()317g = prod([G.gen(i)**(self.list()[i]) for i in range(G.ngens())])318gap.eval("l:=One(Rationals)") ## trick needed for LL line below to keep Sage from parsing319s1 = "gens := GeneratorsOfGroup(%s)"%G._gap_init_()320gap.eval(s1)321for i in range(len(gens)):322cmd = ("%s := gens["+str(i+1)+"]")%gens[i]323gap.eval(cmd)324s2 = "g0:=%s; gensH:=%s"%(str(g),words)325gap.eval(s2)326s3 = 'G:=Group(gens); H:=Group(gensH)'327gap.eval(s3)328phi = gap.eval("hom:=EpimorphismFromFreeGroup(H)")329l1 = gap.eval("ans:=PreImagesRepresentative(hom,g0)")330l2 = copy.copy(l1)331l4 = []332l3 = l1.split("*")333for i in range(1,len(words)+1):334l2 = l2.replace("x"+str(i),"("+str(words[i-1])+")")335l3 = eval(gap.eval("L3:=ExtRepOfObj(ans)"))336nn = eval(gap.eval("n:=Int(Length(L3)/2)"))337LL1 = eval(gap.eval("L4:=List([l..n],i->L3[2*i])")) ## note the l not 1338LL2 = eval(gap.eval("L5:=List([l..n],i->L3[2*i-1])")) ## note the l not 1339if display:340s = str(g)+" = "+add_strings(["("+str(words[LL2[i]-1])+")^"+str(LL1[i])+"*" for i in range(nn)])341m = len(s)342print " ",s[:m-1],"\n"343return [[words[LL2[i]-1],LL1[i]] for i in range(nn)]344345346347348349350