def __classcall_private__(cls, generators, ambient=None, one=None, mul=operator.mul, category=None): """ Parse and straighten the arguments; figure out the category. TESTS:: sage: from sage.monoids.automatic_semigroup import AutomaticSemigroup sage: R = IntegerModRing(9) sage: M = AutomaticSemigroup((), one=R.one()) sage: M.ambient() == R True sage: AutomaticSemigroup((0,)).category() Join of Category of finitely generated semigroups and Category of subquotients of semigroups and Category of commutative magmas and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=1).category() Join of Category of subquotients of monoids and Category of commutative monoids and Category of finitely generated semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=0).category() Join of Category of commutative monoids and Category of finitely generated semigroups and Category of subquotients of semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), mul=operator.add).category() Join of Category of semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=0, mul=operator.add).category() Join of Category of monoids and Category of subobjects of sets sage: S5 = SymmetricGroup(5) sage: AutomaticSemigroup([S5((1,2))]).category() Join of Category of finite groups and Category of subquotients of monoids and Category of finite finitely generated semigroups and Category of subquotients of finite sets and Category of subobjects of sets .. TODO:: One would want a subsemigroup of a group to be automatically a subgroup (in ``Groups().Subobjects()``). """ generators = Family(generators) if ambient is None: # Try to guess the ambient semigroup from the generators or the unit if generators.cardinality() > 0: ambient = generators.first().parent() elif one is not None: ambient = one.parent() else: raise ValueError( "AutomaticSemigroup requires at least one generator or `one` to determine the ambient space" ) elif ambient not in Sets: raise ValueError("ambient (=%s) should be a set" % ambient) # if mul is not operator.mul and category.is_subcategory(Monoids().Subobjects()) error if one is None and category is not None: if category.is_subcategory(Monoids().Subobjects()): one = ambient.one() elif category.is_subcategory(Monoids()): raise ValueError( "For a monoid which is just a subsemigroup, the unit should be specified" ) # Try to determine the most specific category # This logic should be in the categories if mul is operator.mul: default_category = Semigroups().FinitelyGenerated() if one is not None and one == ambient.one(): default_category = default_category.Unital() if ambient in Semigroups().Commutative(): default_category = default_category.Commutative() if ambient in Groups().Finite(): default_category = default_category & Groups() else: default_category = Sets() if ambient in Sets().Finite(): default_category = default_category.Finite() default_category = default_category.Subobjects() & Semigroups() if one is not None: default_category = default_category.Unital() cls = AutomaticMonoid if category is None: category = default_category else: category = default_category & category return super(AutomaticSemigroup, cls).__classcall__(cls, generators, ambient=ambient, one=one, mul=mul, category=category)
class Surface_polygons_and_gluings(Surface): r""" Similarity surface build from a list of polygons and gluings. """ def __init__(self, *args): r""" The constructor either acts as a copy constructor for a finite surface, or you can pass two options: polygons and identifications. INPUT: - ``polygons`` - a family of polygons (might be a list, a dictionnary label -> polygon or more generally a Family) - ``identifications`` - the identification of the edges. A list of pairs ((p0,e0),(p1,e1)). """ if len(args)==2: polygons=args[0] identifications=args[1] self._polygons = Family(polygons) if self._polygons.cardinality() == 0: raise ValueError("there should be at least one polygon") self._field = self._polygons.an_element().parent().field() n = 0 for p in self._polygons: if p.parent().field() != self._field: raise ValueError("the field must be the same for all polygons") n += 1 if n > 10: # the number of polygons may be infinite... break if isinstance(identifications, (list,dict)): edge_identifications = {} if isinstance(identifications, dict): it = identifications.iteritems() else: it = iter(identifications) for e0,e1 in it: edge_identifications[e0] = e1 # Check that e0 makes sense. assert e0[1]>=0 and e0[1]<self._polygons[e0[0]].num_edges() # Check that e1 makes sense. assert e1[1]>=0 and e1[1]<self._polygons[e1[0]].num_edges() if e1 in edge_identifications: assert edge_identifications[e1] == e0 else: edge_identifications[e1] = e0 else: edge_identifications = identifications self._edge_identifications = edge_identifications elif len(args)==1: # Copy constructor for finite surface. s = args[0] if not s.is_finite(): raise ValueError("Can only copy finite surface.") polygons = {} edge_identifications = {} for label,polygon in s.label_polygon_iterator(): polygons[label]=polygon for edge in xrange(polygon.num_edges()): edge_identifications[(label,edge)]=s.opposite_edge(label,edge) self._field = s.base_ring() self._polygons=Family(polygons) self._edge_identifications = edge_identifications else: raise ValueError("Can only be called with one or two arguments.") # display everything by default # I don't think we should be doing this by default whenever we create # a surface. -Pat #adj = [] #todo = [self.base_label()] #labs = set(self.polygon_labels()) #labs.remove(self.base_label()) #while todo: # p1 = todo.pop() # for e1 in range(self.polygon(p1).num_edges()): # p2,e2 = self.opposite_edge(p1,e1) # if p2 in labs: # labs.remove(p2) # adj.append((p1,e1)) #self._plot_options = {'adjacencies': adj} def is_finite(self): r""" Return whether or not the surface is finite. """ from sage.rings.infinity import Infinity return self._polygons.cardinality() != Infinity def base_ring(self): return self._field def polygon_labels(self): from sage.combinat.words.alphabet import build_alphabet return build_alphabet(self._polygons.keys()) def base_label(self): return self.polygon_labels().an_element() def polygon(self, lab): r""" Return the polygon with label ``lab``. """ return self._polygons[lab] def opposite_edge(self, p, e): if (p,e) not in self._edge_identifications: e = e % self._polygons[p].num_edges() if (p,e) not in self._edge_identifications: raise ValueError("The pair"+str((p,e))+" is not a valid edge identifier.") return self._edge_identifications[(p,e)]
def __classcall_private__(cls, generators, ambient=None, one=None, mul=operator.mul, category=None): """ Parse and straighten the arguments; figure out the category. TESTS:: sage: from sage.monoids.automatic_semigroup import AutomaticSemigroup sage: R = IntegerModRing(9) sage: M = AutomaticSemigroup((), one=R.one()) sage: M.ambient() == R True sage: AutomaticSemigroup((0,)).category() Join of Category of finitely generated semigroups and Category of subquotients of semigroups and Category of commutative magmas and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=1).category() Join of Category of subquotients of monoids and Category of commutative monoids and Category of finitely generated semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=0).category() Join of Category of commutative monoids and Category of finitely generated semigroups and Category of subquotients of semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), mul=operator.add).category() Join of Category of semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=0, mul=operator.add).category() Join of Category of monoids and Category of subobjects of sets sage: S5 = SymmetricGroup(5) sage: AutomaticSemigroup([S5((1,2))]).category() Join of Category of finite groups and Category of subquotients of monoids and Category of finite finitely generated semigroups and Category of subquotients of finite sets and Category of subobjects of sets .. TODO:: One would want a subsemigroup of a group to be automatically a subgroup (in ``Groups().Subobjects()``). """ generators = Family(generators) if ambient is None: # Try to guess the ambient semigroup from the generators or the unit if generators.cardinality() > 0: ambient = generators.first().parent() elif one is not None: ambient = one.parent() else: raise ValueError( "AutomaticSemigroup requires at least one generator or `one` to determine the ambient space" ) elif ambient not in Sets: raise ValueError("ambient (=%s) should be a set" % ambient) # if mul is not operator.mul and category.is_subcategory(Monoids().Subobjects()) error if one is None and category is not None: if category.is_subcategory(Monoids().Subobjects()): one = ambient.one() elif category.is_subcategory(Monoids()): raise ValueError("For a monoid which is just a subsemigroup, the unit should be specified") # Try to determine the most specific category # This logic should be in the categories if mul is operator.mul: default_category = Semigroups().FinitelyGenerated() if one is not None and one == ambient.one(): default_category = default_category.Unital() if ambient in Semigroups().Commutative(): default_category = default_category.Commutative() if ambient in Groups().Finite(): default_category = default_category & Groups() else: default_category = Sets() if ambient in Sets().Finite(): default_category = default_category.Finite() default_category = default_category.Subobjects() & Semigroups() if one is not None: default_category = default_category.Unital() cls = AutomaticMonoid if category is None: category = default_category else: category = default_category & category return super(AutomaticSemigroup, cls).__classcall__( cls, generators, ambient=ambient, one=one, mul=mul, category=category )