Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/groups/abelian_gps/dual_abelian_group_element.py
4105 views
1
"""
2
Elements (characters) of the dual group of a finite Abelian group.
3
4
AUTHORS:
5
- David Joyner (2006-07); based on abelian_group_element.py.
6
- David Joyner (2006-10); modifications suggested by William Stein.
7
8
EXAMPLES:
9
sage: F = AbelianGroup(5,[2, 3, 5, 7, 8], names="abcde")
10
sage: a,b,c,d,e = F.gens()
11
sage: Fd = DualAbelianGroup(F, names = "ABCDE")
12
sage: A,B,C,D,E = Fd.gens()
13
sage: A*B^2*D^7
14
A*B^2
15
sage: A(a) ## random last few digits
16
-1.0000000000000000 + 0.00000000000000013834419720915037*I
17
sage: B(b)
18
-0.500000000000000 + 0.866025403784439*I
19
sage: A(a*b) ## random last few digits
20
-1.0000000000000000 + 0.00000000000000013834419720915037*I
21
sage: (A*B*C^2*D^20*E^65).list()
22
[1, 1, 2, 6, 1]
23
sage: B^(-1)
24
B^2
25
26
It is important to note that lists are mutable and the
27
returned list is not a copy. As a result, reassignment
28
of an element of the list changes the object.
29
30
sage: X = A*B*C^2*D^2*E^-6
31
sage: X.list()
32
[1, 1, 2, 2, 2]
33
sage: X.list()[1] = -1
34
sage: X
35
A*B^-1*C^2*D^2*E^2
36
37
"""
38
39
###########################################################################
40
# Copyright (C) 2006 William Stein <[email protected]>
41
# Copyright (C) 2006 David Joyner <[email protected]>
42
#
43
# Distributed under the terms of the GNU General Public License (GPL)
44
# http://www.gnu.org/licenses/
45
###########################################################################
46
47
import operator
48
49
from sage.rings.integer import Integer
50
from sage.structure.element import MonoidElement
51
from sage.rings.infinity import infinity
52
from sage.rings.arith import *
53
from sage.misc.misc import prod
54
from sage.misc.functional import exp
55
from sage.rings.complex_field import is_ComplexField
56
57
58
def add_strings(x, z=0):
59
"""
60
This was in sage.misc.misc but commented out. Needed to add
61
lists of strings in the word_problem method below.
62
63
Return the sum of the elements of x. If x is empty,
64
return z.
65
66
INPUT:
67
x -- iterable
68
z -- the "0" that will be returned if x is empty.
69
70
OUTPUT:
71
object
72
"""
73
if len(x) == 0:
74
return z
75
if not isinstance(x, list):
76
m = x.__iter__()
77
y = m.next()
78
return reduce(operator.add, m, y)
79
else:
80
return reduce(operator.add, x[1:], x[0])
81
82
83
def is_DualAbelianGroupElement(x):
84
return isinstance(x, DualAbelianGroupElement)
85
86
class DualAbelianGroupElement(MonoidElement):
87
def __init__(self, F, X):
88
"""
89
Create an element X of the DualAbelianGroup of F.
90
91
EXAMPLES:
92
sage: F = AbelianGroup(3,[7,8,9])
93
sage: Fd = DualAbelianGroup(F,names="ABC")
94
sage: A,B,C = Fd.gens()
95
sage: A*B^-1 in Fd
96
True
97
98
"""
99
MonoidElement.__init__(self, F)
100
self.__repr = None
101
G = F.group()
102
n = G.ngens()
103
if isinstance(X, (int, Integer)) and X == 1:
104
self.__element_vector = [ 0 for i in range(n) ]
105
elif isinstance(X, list):
106
if len(X) != n:
107
raise IndexError, \
108
"Argument length (= %s) must be %s."%(len(X), n)
109
self.__element_vector = X
110
else:
111
raise TypeError, "Argument X (= %s) is of wrong type."%X
112
113
def list(self):
114
"""
115
Return (a reference to) the underlying list used to represent
116
this element. If this is a word in an abelian group on $n$
117
generators, then this is a list of nonnegative integers of
118
length $n$.
119
120
EXAMPLES:
121
sage: F = AbelianGroup(5,[2, 3, 5, 7, 8], names="abcde")
122
sage: a,b,c,d,e = F.gens()
123
sage: Ad = DualAbelianGroup(F, names = "ABCDE")
124
sage: A,B,C,D,E = Ad.gens()
125
sage: (A*B*C^2*D^20*E^65).list()
126
[1, 1, 2, 6, 1]
127
sage: X = A*B*C^2*D^2*E^-6
128
sage: X.list()
129
[1, 1, 2, 2, 2]
130
sage: X.list()[1] = -1
131
sage: X
132
A*B^-1*C^2*D^2*E^2
133
"""
134
return self.__element_vector
135
136
def _repr_(self):
137
s = ""
138
A = self.parent()
139
n = A.ngens()
140
x = A.variable_names()
141
v = self.list()
142
for i in range(n):
143
if v[i] == 0:
144
continue
145
elif v[i] == 1:
146
if len(s) > 0: s += "*"
147
s += "%s"%x[i]
148
else:
149
if len(s) > 0: s += "*"
150
s += "%s^%s"%(x[i],v[i])
151
if len(s) == 0: s = "1"
152
return s
153
154
def __mul__(self, y):
155
#Same as _mul_ in AbelianGroupElement
156
157
M = self.parent()
158
n = M.ngens()
159
invs = M.invariants()
160
z = M(1)
161
xelt = self.list()
162
yelt = y.list()
163
zelt = [ xelt[i]+yelt[i] for i in range(len(xelt)) ]
164
if len(invs) >= n:
165
L = []
166
for i in range(len(xelt)):
167
if invs[i]!=0:
168
L.append(zelt[i]%invs[i])
169
if invs[i]==0:
170
L.append(zelt[i])
171
z.__element_vector = L
172
#print z.__element_vector
173
if len(invs) < n:
174
L1 = []
175
for i in range(len(invs)):
176
if invs[i]!=0:
177
L1.append(zelt[i]%invs[i])
178
if invs[i]==0:
179
L1.append(zelt[i])
180
L2 = [ zelt[i] for i in range(len(invs),len(xelt)) ]
181
z.__element_vector = L1+L2
182
return M(z.__element_vector)
183
184
def __pow__(self, n):
185
"""
186
requires that len(invs) = n
187
"""
188
if not isinstance(n, (int, long, Integer)):
189
raise TypeError, "Argument n (= %s) must be an integer."%n
190
n = int(n)
191
M = self.parent()
192
N = M.ngens()
193
invs = M.invariants()
194
if n < 0:
195
L =[n*self.list()[i]%M.gen(i).order() for i in range(M.ngens())]
196
return prod([M.gen(i)**L[i] for i in range(M.ngens())])
197
#m = LCM(invs) ## Not very efficient version
198
#pw = (n)%m
199
#x = self**pw
200
#return x
201
elif n == 0:
202
return M(1)
203
elif n == 1:
204
return self
205
elif n == 2:
206
return self * self
207
k = n//2
208
return self**k * self**(n-k)
209
210
def __cmp__(self,other):
211
if (self.list() != other.list()):
212
return -1
213
return 0
214
215
def order(self):
216
"""
217
Returns the (finite) order of this element.
218
219
EXAMPLES:
220
sage: F = AbelianGroup(3,[7,8,9])
221
sage: Fd = DualAbelianGroup(F)
222
sage: A,B,C = Fd.gens()
223
sage: (B*C).order()
224
72
225
"""
226
M = self.parent()
227
#print self, M
228
if self == M(1):
229
return Integer(1)
230
invs = M.invariants()
231
if self in M.gens():
232
o = invs[list(M.gens()).index(self)]
233
if o == 0:
234
return infinity
235
return o
236
L = list(self.list())
237
N = LCM([invs[i]/GCD(invs[i],L[i]) for i in range(len(invs)) if L[i]!=0]) ####### error here????
238
if N == 0:
239
return infinity
240
else:
241
return N
242
243
def __call__(self,g):
244
"""
245
Computes the value of a character self on a group element
246
g (g must be an element of self.group())
247
248
EXAMPLES:
249
sage: F = AbelianGroup(5, [2,3,5,7,8], names="abcde")
250
sage: a,b,c,d,e = F.gens()
251
sage: Fd = DualAbelianGroup(F, names="ABCDE")
252
sage: A,B,C,D,E = Fd.gens()
253
sage: A*B^2*D^7
254
A*B^2
255
sage: A(a) ## random last few digits
256
-1.0000000000000000 + 0.00000000000000013834419720915037*I
257
sage: B(b) ## random last few digits
258
-0.49999999999999983 + 0.86602540378443871*I
259
sage: A(a*b) ## random last few digits
260
-1.0000000000000000 + 0.00000000000000013834419720915037*I
261
"""
262
F = self.parent().base_ring()
263
expsX = list(self.list())
264
expsg = list(g.list())
265
invs = self.parent().invariants()
266
N = LCM(invs)
267
if is_ComplexField(F):
268
from sage.symbolic.constants import pi
269
I = F.gen()
270
PI = F(pi)
271
ans = prod([exp(2*PI*I*expsX[i]*expsg[i]/invs[i]) for i in range(len(expsX))])
272
return ans
273
ans = F(1) ## assumes F is the cyclotomic field
274
zeta = F.gen()
275
#print F,zeta
276
for i in range(len(expsX)):
277
inv_noti = N/invs[i]
278
ans = ans*zeta**(expsX[i]*expsg[i]*inv_noti)
279
return ans
280
281
def word_problem(self, words, display=True):
282
"""
283
This is a rather hackish method and is included for completeness.
284
285
The word problem for an instance of DualAbelianGroup as it can
286
for an AbelianGroup. The reason why is that word problem
287
for an instance of AbelianGroup simply calls GAP (which
288
has abelian groups implemented) and invokes "EpimorphismFromFreeGroup"
289
and "PreImagesRepresentative". GAP does not have duals of
290
abelian groups implemented. So, by using the same name
291
for the generators, the method below converts the problem for
292
the dual group to the corresponding problem on the group
293
itself and uses GAP to solve that.
294
295
EXAMPLES:
296
sage: G = AbelianGroup(5,[3, 5, 5, 7, 8],names="abcde")
297
sage: Gd = DualAbelianGroup(G,names="abcde")
298
sage: a,b,c,d,e = Gd.gens()
299
sage: u = a^3*b*c*d^2*e^5
300
sage: v = a^2*b*c^2*d^3*e^3
301
sage: w = a^7*b^3*c^5*d^4*e^4
302
sage: x = a^3*b^2*c^2*d^3*e^5
303
sage: y = a^2*b^4*c^2*d^4*e^5
304
sage: e.word_problem([u,v,w,x,y],display=False)
305
[[b^2*c^2*d^3*e^5, 245]]
306
307
The command e.word_problem([u,v,w,x,y],display=True) returns
308
the same list but also prints $e = (b^2*c^2*d^3*e^5)^245$.
309
310
"""
311
## First convert the problem to one using AbelianGroups
312
import copy
313
from sage.groups.abelian_gps.abelian_group import AbelianGroup
314
from sage.interfaces.all import gap
315
M = self.parent()
316
G = M.group()
317
gens = M.variable_names()
318
g = prod([G.gen(i)**(self.list()[i]) for i in range(G.ngens())])
319
gap.eval("l:=One(Rationals)") ## trick needed for LL line below to keep Sage from parsing
320
s1 = "gens := GeneratorsOfGroup(%s)"%G._gap_init_()
321
gap.eval(s1)
322
for i in range(len(gens)):
323
cmd = ("%s := gens["+str(i+1)+"]")%gens[i]
324
gap.eval(cmd)
325
s2 = "g0:=%s; gensH:=%s"%(str(g),words)
326
gap.eval(s2)
327
s3 = 'G:=Group(gens); H:=Group(gensH)'
328
gap.eval(s3)
329
phi = gap.eval("hom:=EpimorphismFromFreeGroup(H)")
330
l1 = gap.eval("ans:=PreImagesRepresentative(hom,g0)")
331
l2 = copy.copy(l1)
332
l4 = []
333
l3 = l1.split("*")
334
for i in range(1,len(words)+1):
335
l2 = l2.replace("x"+str(i),"("+str(words[i-1])+")")
336
l3 = eval(gap.eval("L3:=ExtRepOfObj(ans)"))
337
nn = eval(gap.eval("n:=Int(Length(L3)/2)"))
338
LL1 = eval(gap.eval("L4:=List([l..n],i->L3[2*i])")) ## note the l not 1
339
LL2 = eval(gap.eval("L5:=List([l..n],i->L3[2*i-1])")) ## note the l not 1
340
if display:
341
s = str(g)+" = "+add_strings(["("+str(words[LL2[i]-1])+")^"+str(LL1[i])+"*" for i in range(nn)])
342
m = len(s)
343
print " ",s[:m-1],"\n"
344
return [[words[LL2[i]-1],LL1[i]] for i in range(nn)]
345
346
347
348
349
350