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)
Beispiel #2
0
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)]
Beispiel #3
0
    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
        )