@@ -160,6 +160,9 @@ def __iter__(self):
160160 for g in self .group_elems :
161161 if g != self .e : yield g
162162
163+ def __contains__ (self , item ):
164+ return item in self .group_elems
165+
163166 def __hash__ (self ):
164167 return hash (self .Set ) ^ hash (self .bin_op )
165168
@@ -258,15 +261,22 @@ def __mul__(self, other):
258261 def generate (self , elems ):
259262 """
260263 Returns the subgroup of self generated by GroupElems elems
264+
265+ If any of the items aren't already GroupElems, we will try to convert
266+ them to GroupElems before continuing.
261267
262268 elems must be iterable
263269 """
264- if not Set (elems ) <= self .group_elems :
270+
271+ elems = Set (g if isinstance (g , GroupElem ) else GroupElem (g , self ) \
272+ for g in elems )
273+
274+ if not elems <= self .group_elems :
265275 raise ValueError ("elems must be a subset of self.group_elems" )
266276 if len (elems ) == 0 :
267277 raise ValueError ("elems must have at least one element" )
268278
269- oldG = Set ( elems )
279+ oldG = elems
270280 while True :
271281 newG = oldG | Set (a * b for a in oldG for b in oldG )
272282 if oldG == newG : break
@@ -288,6 +298,80 @@ def subgroups(self):
288298
289299 return old_sgs
290300
301+ def generators (self ):
302+ """
303+ Returns a list of GroupElems that generate self, with length
304+ at most log_2(len(self)) + 1
305+ """
306+
307+ result = [self .e .elem ]
308+ H = self .generate (result )
309+
310+ while len (H ) < len (self ):
311+ result .append ((self .Set - H .Set ).pick ())
312+ H = self .generate (result )
313+
314+ # The identity is always a redundant generator in nontrivial Groups
315+ if len (self ) != 1 :
316+ result = result [1 :]
317+
318+ return [GroupElem (g , self ) for g in result ]
319+
320+ def find_isomorphism (self , other ):
321+ """
322+ Returns an isomorphic GroupHomomorphism between self and other,
323+ or None if self and other are not isomorphic
324+
325+ Uses Tarjan's algorithm, running in O(n^(log n + O(1))) time, but
326+ runs a lot faster than that if the group has a small generating set.
327+ """
328+ if not isinstance (other , Group ):
329+ raise TypeError ("other must be a Group" )
330+
331+ if len (self ) != len (other ) or self .is_abelian () != other .is_abelian ():
332+ return None
333+
334+ # Try to match the generators of self with some subset of other
335+ A = self .generators ()
336+ for B in itertools .permutations (other , len (A )):
337+
338+ func = dict (itertools .izip (A , B )) # the mapping
339+ counterexample = False
340+ while not counterexample :
341+
342+ # Loop through the mapped elements so far, trying to extend the
343+ # mapping or else find a counterexample
344+ noobs = {}
345+ for g , h in itertools .product (func , func ):
346+ if g * h in func :
347+ if func [g ] * func [h ] != func [g * h ]:
348+ counterexample = True
349+ break
350+ else :
351+ noobs [g * h ] = func [g ] * func [h ]
352+
353+ # If we've mapped all the elements of self, then it's a
354+ # homomorphism provided we haven't seen any counterexamples.
355+ if len (func ) == len (self ):
356+ break
357+
358+ # Make sure there aren't any collisions before updating
359+ imagelen = len (set (noobs .values ()) | set (func .values ()))
360+ if imagelen != len (noobs ) + len (func ):
361+ counterexample = True
362+ func .update (noobs )
363+
364+ if not counterexample :
365+ return GroupHomomorphism (self .group_elems , other .group_elems , \
366+ lambda x : func [x ])
367+
368+ return None
369+
370+ def is_isomorphic (self , other ):
371+ """Checks if self and other are isomorphic"""
372+ return bool (self .find_isomorphism (other ))
373+
374+
291375class GroupHomomorphism (Function ):
292376 """
293377 The definition of a Group Homomorphism
@@ -342,13 +426,32 @@ def is_isomorphism(self):
342426
343427
344428def Zn (n ):
345- """ Returns the cylic group of order n"""
429+ """Returns the cylic group of order n"""
346430 G = Set (range (n ))
347431 bin_op = Function (G * G , G , lambda x : (x [0 ] + x [1 ]) % n )
348432 return Group (G , bin_op )
349433
350434def Sn (n ):
351- """ Returns the symmetric group of order n! """
435+ """Returns the symmetric group of order n! """
352436 G = Set (g for g in itertools .permutations (range (n )))
353437 bin_op = Function (G * G , G , lambda x : tuple (x [0 ][j ] for j in x [1 ]))
354438 return Group (G , bin_op )
439+
440+ def Dn (n ):
441+ """Returns the dihedral group of order 2n """
442+ G = Set ("%s%d" % (l , x ) for l in "RS" for x in xrange (n ))
443+ def multiply_symmetries (x ):
444+ l1 , l2 = x [0 ][0 ], x [1 ][0 ]
445+ x1 , x2 = int (x [0 ][1 :]), int (x [1 ][1 :])
446+ if l1 == "R" :
447+ if l2 == "R" :
448+ return "R%d" % ((x1 + x2 ) % n )
449+ else :
450+ return "S%d" % ((x1 + x2 ) % n )
451+ else :
452+ if l2 == "R" :
453+ return "S%d" % ((x1 - x2 ) % n )
454+ else :
455+ return "R%d" % ((x1 - x2 ) % n )
456+ return Group (G , Function (G * G , G , multiply_symmetries ))
457+
0 commit comments