Ticket #10194: trac_10194-factories_policy-fh.patch

File trac_10194-factories_policy-fh.patch, 57.1 KB (added by Florent Hivert, 11 years ago)
  • doc/en/reference/structure.rst

    # HG changeset patch
    # User Florent Hivert <Florent.Hivert@univ-rouen.fr>
    # Date 1333053022 -7200
    # Node ID 9876e9de7b012ac492d03b6c8d04b3f8688d23aa
    # Parent  833ab836fcf16ca4c53e0e06186457f6d7af4e04
    [mq]: trac_10194-factories_policy-fh.patch
    
    diff --git a/doc/en/reference/structure.rst b/doc/en/reference/structure.rst
    a b Basic Structures 
    1818   sage/structure/sequence
    1919   sage/structure/element_wrapper
    2020
     21
     22   sage/structure/set_factories
     23   sage/structure/set_factories_example
    2124   sage/sets/cartesian_product
    2225   sage/sets/family
    2326   sage/sets/set
  • sage/misc/sagedoc.py

    diff --git a/sage/misc/sagedoc.py b/sage/misc/sagedoc.py
    a b math_substitutes = [ # don't forget lead 
    4444    ('\\cdot', ' *'),
    4545    (' \\times', ' x'),
    4646    ('\\times', ' x'),
     47    ('\\quad', '  '),
     48    ('\\qquad', '    '),
     49    ('\\mid', '|'),
    4750    ('\\backslash','\\'),
    4851    ('\\mapsto', ' |--> '),
    4952]
  • new file sage/structure/set_factories.py

    diff --git a/sage/structure/set_factories.py b/sage/structure/set_factories.py
    new file mode 100644
    - +  
     1r"""
     2Set factories
     3=============
     4
     5A *set factory* `F` is device, whose goal is to construct :class:`Parent` `P`
     6which models subsets of a big set `S`. Typically, the `P` s are constructed
     7within families obtained by putting a bunch of constraints `cons` on the
     8elements of the set `S`. In such a hierarchy of subsets, one needs to have a
     9fine and easy control on the elements construction. That is, one often needs
     10that `P` constructs elements in a subclass of its usual class for element. On
     11the contrary, one also often needs `P` to be a facade parent, meaning that `P`
     12construct element whose actual parent is not `P` itself (see
     13:func:`~sage.categories.facade_sets.Facade`).
     14
     15The role of a set factory is twofold:
     16
     17- *manage a database* of constructors for the different parents `P = F(cons)`
     18  depending on the various kinds of constraints `cons`. Note: currently there
     19  is no real support for that. We are gathering use case before fixing the
     20  interface.
     21
     22- ensure that the elements `e = P(...)` created by the different parents
     23  follows a consistent policy concerning their *class and parent*.
     24
     25.. RUBRIC:: Basic usage: constructing parents through a factory
     26
     27The file :mod:`sage.structure.set_factories_example` shows an example of a
     28:class:`SetFactory` together with typical implementation. Note that the
     29written code is intentionnally kept minimal, many thing and in particular
     30several iterator could be written in a more efficient ways.
     31
     32Consider the set `S` of couple `(x,y)` with `x` and `y` in `I:=\{0,1,2,3,4\}`.
     33We represent the element of `S` as 2-elements tuple, wrapped in a class
     34:class:`~.set_factories_example.XYPair` deriving from :class:`ElementWrapper`.
     35You can create a :class:`~.set_factories_example.XYPair` with any
     36:class:`Parent`::
     37
     38    sage: from sage.structure.set_factories import *
     39    sage: from sage.structure.set_factories_example import *
     40    sage: p = XYPair(Parent(), (0,1)); p
     41    (0, 1)
     42
     43Now, given `(a, b)\in S` we want to consider the following subsets of
     44`S`
     45
     46.. MATH::
     47
     48    S_a := \{(x,y) \in S \mid x = a\},\qquad
     49    S^b := \{(x,y) \in S \mid y = b\},\qquad
     50    S_a^b := \{(x,y) \in S \mid x = a, y = b\}.
     51
     52The constraints considered here are admittedly trivial. In a realistic
     53examples, there would be much more of them. And for some sets of constraints
     54no good enumeration algorithms would be known.
     55
     56In Sage, those sets are constructed passing the constraints to the factory. We
     57first create the set with no constraints at all::
     58
     59    sage: XYPairs
     60    Factory for XY pairs
     61    sage: S = XYPairs(); S.list()
     62    [(0, 0), (1, 0), ..., (4, 0), (0, 1), (1, 1), ..., (3, 4), (4, 4)]
     63    sage: S.cardinality()
     64    25
     65
     66Lets construct `S_2`, `S^3` and `S_2^3`::
     67
     68    sage: Sx2 = XYPairs(x=2); Sx2.list()
     69    [(2, 0), (2, 1), (2, 2), (2, 3), (2, 4)]
     70    sage: Sy3 = XYPairs(y=3); Sy3.list()
     71    [(0, 3), (1, 3), (2, 3), (3, 3), (4, 3)]
     72    sage: S23 = XYPairs(x=2, y=3); S23.list()
     73    [(2, 3)]
     74
     75Set factories provide an alternative way to build subsets of and already
     76constructed set: each set constructed by a factory has a method
     77:meth:`~SetFactoryParent.subset` which accept new constraints. Set
     78constructed by the factory or the :meth:`~SetFactoryParent.subset` methods are
     79identical::
     80
     81    sage: Sx2s = S.subset(x=2); Sx2 is Sx2s
     82    True
     83    sage: Sx2.subset(y=3) is S23
     84    True
     85
     86Is is not possible to change an already given constraints::
     87
     88    sage: S23.subset(y=5)
     89    Traceback (most recent call last):
     90    ...
     91    TypeError: __call__() got multiple values for keyword argument 'y'
     92
     93.. RUBRIC:: Constructing custom elements: policies
     94
     95We now come to the point of factories: constructing custom elements. By
     96default, the writer of :func:`~.set_factories_example.XYPairs` decided that
     97the parent ``Sx2``, ``Sy3`` and ``S23`` are facade for parent ``S``. This
     98means that each elements constructed by those subsets behaves as if they where
     99directly constructed by ``S`` istelf::
     100
     101    sage: Sx2.an_element().parent()
     102    AllPairs
     103    sage: el = Sx2.an_element()
     104    sage: el.parent() is S
     105    True
     106    sage: type(el) is S.element_class
     107    True
     108
     109This is not always desirable. The device which decide how to construct an
     110element is called a *policy* (see :class:`SetFactoryPolicy`). Each factory
     111should have a default policy. Here is the policy of
     112:func:`~.set_factories_example.XYPairs`::
     113
     114    sage: XYPairs._default_policy
     115    Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     116
     117This means that with the current policy, the parent builds element with class
     118``XYPair`` and parent ``AllPairs`` which is itself constructed by calling the
     119factory :func:`~.set_factories_example.XYPairs` with constraints ``()``. There
     120is a lot of flexibility to change that. We now illustrate how to make two
     121different choices.
     122
     1231 - In a first use case, we want to add some method to the constructed
     124elements. As illustration, we add here a new method ``sum`` which returns
     125`x+y`. We therefore inherit from :class:`~.set_factories_example.XYPair`::
     126
     127    sage: class NewXYPair(XYPair):
     128    ...       def sum(self):
     129    ...           return sum(self.value)
     130    sage: el = NewXYPair(Parent(), (2,3))
     131    sage: el.sum()
     132    5
     133
     134We now want to have subsets generating those new elements while still having a
     135single real parent (the one with no constraints) for each element. The
     136corresponding policy is called :class:`TopMostParentPolicy`. It takes tree
     137parameters:
     138
     139- the factory;
     140- the parameters for void constraints;
     141- the class used for elements.
     142
     143Calling the factory with this policy returns a new sets which builds his
     144elements with the new policy::
     145
     146    sage: newpolicy = TopMostParentPolicy(XYPairs, (), NewXYPair)
     147    sage: newS = XYPairs(policy = newpolicy)
     148    sage: el = newS.an_element(); el
     149    (0, 0)
     150    sage: el.sum()
     151    0
     152    sage: el.parent() is newS
     153    True
     154
     155Subsets now inherits the policy::
     156
     157    sage: newS2 = newS.subset(x=2)
     158    sage: el2 = newS2.an_element(); el2
     159    (2, 0)
     160    sage: el2.sum()
     161    2
     162    sage: el2.parent() is newS
     163    True
     164
     1652 - In a second use case, we want the elements to remember which parent
     166created them. The corresponding policy is called
     167:class:`SelfParentPolicy`. It takes only two parameters:
     168
     169- the factory;
     170- the class used for elements.
     171
     172Here is an examples::
     173
     174    sage: selfpolicy = SelfParentPolicy(XYPairs, NewXYPair)
     175    sage: selfS = XYPairs(policy = selfpolicy)
     176    sage: el = selfS.an_element();
     177    sage: el.parent() is selfS
     178    True
     179
     180Now subsets are the parent of the element they created::
     181
     182    sage: selfS2 = selfS.subset(x=2)
     183    sage: el2 = selfS2.an_element()
     184    sage: el2.parent() is newS
     185    False
     186    sage: el2.parent() is selfS2
     187    True
     188
     189Here are the currently implemented policies:
     190
     191- :class:`FacadeParentPolicy`: reuse an existing parent together with
     192  its element_class
     193
     194- :class:`TopMostParentPolicy`: use a parent created by the factory itself and
     195  provide a class ``Element`` for it. In this case, we need to specify for
     196  which sets of constraints the constructed parent needs to be provided with
     197  a class ``Element``.
     198
     199- :class:`SelfParentPolicy`: provide systematically Element and
     200  element_class and ensure that the parent is ``self``.
     201
     202.. TODO:: Generalize :class:`TopMostParentPolicy` to be able to have several
     203    topmost parent.
     204
     205.. RUBRIC:: Technicalities: how policies inform parents
     206
     207Parent built from factories should inherits from
     208:class:`SetFactoryParent`. This class provide a few methods related to
     209factories and policies. The ``__init__`` method of :class:`SetFactoryParent`
     210must be provided with the set of constraints and the policy. A parent built
     211from a factory must creates element trough a call to the method
     212``_element_constructor_``. The current way policies inform parents how to
     213builds their elements is through policy category (see
     214:class:`AbstractPolicyCategory`). We (ab)use the category system to provide the parent with a category whose role is to set ``_element_constructor_`` and the related methods and attributes appropriately::
     215
     216    sage: S = XYPairs()
     217    sage: S.categories()
     218    [..., Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>, ...]
     219
     220    sage: S2 = S.subset(x=2)
     221    sage: S2.categories()
     222    [..., Policy category with facade parent AllPairs, ...]
     223
     224This category is computed from the constraints by the method
     225:meth:`SetFactoryPolicy.category` of the policy::
     226
     227    sage: S.constraints()
     228    ()
     229    sage: XYPairs._default_policy.category(())
     230    Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     231
     232    sage: S2.constraints()
     233    (2,)
     234    sage: XYPairs._default_policy.category((2,))
     235    Policy category with facade parent AllPairs
     236
     237Two concrete policy category deriving from :class:`AbstractPolicyCategory` are
     238available:
     239
     240- :class:`SelfParentPolicyCategory` for usual parent;
     241- :class:`FacadeParentPolicyCategory` for facade parent (see
     242  :func:`~sage.categories.facade_sets.Facade`);
     243
     244
     245.. RUBRIC:: How to write a set factory
     246
     247.. SEEALSO:: :mod:`.set_factories_example` for an example of a factory.
     248
     249Here are the specifications:
     250
     251A parent built from a factory should:
     252
     253- *inherit* from :class:`SetFactoryParent`. It should accept a ``policy``
     254  argument and pass it verbatim to the ``__init__`` method of
     255  :class:`SetFactoryParent` together with the set of constraints;
     256
     257- *create its elements* trough calls to the method ``_element_constructor_``;
     258
     259- *define a method* :class:`SetFactoryParent.check_element` which checks if a
     260  built element indeed belongs to it. The method should accept an extra
     261  keyword parameter called ``check`` specifying which level of check should be
     262  performed. It will only be called when ``bool(check)`` evaluates to
     263  ``True``.
     264
     265The constructor of the elements of a parent from a factory should:
     266
     267- receive the parent as first mandatory argument;
     268
     269- accept an extra optional keyword parameter called ``check`` which is meant
     270  to tell if the input must be checked or not. The precise meaning of
     271  ``check`` is intensionally left vague. The only intend is that if
     272  ``bool(check)`` evaluates to ``False`` no check is performed at all.
     273
     274A factory should
     275
     276- *define a method* ``__call__`` which is responsible for calling the
     277  appropriate parent constructor given the constraints;
     278
     279- *define a method* overloading :meth:`SetFactory.add_constraints` which is
     280  responsible of computing the union of two sets of constraints;
     281
     282- *optionally* define an attribute ``_default_policy`` used by call to pass to
     283  the parent.
     284
     285.. TODO:: There is currently no support for dealing with sets of
     286    constraints. The set factory and the parents must cooperate to
     287    consistently handle them. More support, together with a generic mechanism
     288    to select the appropriate parent class from the constraints, will be added
     289    shortly to sage, as soon as we have gathered sufficiently enough
     290    use-cases.
     291
     292AUTHORS:
     293
     294- Florent Hivert (2011-2012): initial revision
     295"""
     296#*****************************************************************************
     297#  Copyright (C) 2012 Florent Hivert <florent.hivert at lri.fr>
     298#
     299#  Distributed under the terms of the GNU General Public License (GPL)
     300#                  http://www.gnu.org/licenses/
     301#*****************************************************************************
     302
     303
     304from sage.structure.sage_object import SageObject
     305from sage.structure.parent import Parent
     306from sage.structure.unique_representation import UniqueRepresentation
     307from sage.categories.category import Category
     308from sage.categories.sets_cat import Sets
     309from sage.misc.abstract_method import abstract_method
     310from sage.misc.lazy_attribute import lazy_attribute
     311
     312####################################################
     313#                   Factories                      #
     314####################################################
     315
     316class SetFactory(UniqueRepresentation, SageObject):
     317    r"""
     318    This class is currently just a stub we will be using to add more
     319    structures on factories.
     320    """
     321    @abstract_method
     322    def __call__(self, *constraints, **consdict):
     323        r"""
     324        Construct the parent associated with the constraints
     325
     326        Should return a :class:`Parent`.
     327
     328        Currently there is no specification on how constraints are passed as
     329        arguments.
     330
     331        EXAMPLES::
     332
     333            sage: from sage.structure.set_factories_example import XYPairs
     334            sage: XYPairs()
     335            AllPairs
     336            sage: XYPairs(3)
     337            <class 'sage.structure.set_factories_example.PairsX__with_category'>
     338            sage: XYPairs(x=3)
     339            <class 'sage.structure.set_factories_example.PairsX__with_category'>
     340        """
     341
     342    @abstract_method
     343    def add_constraints(self, cons, *args, **opts):
     344        r"""
     345        Add constraints to the set cons
     346
     347        Should return a set of constraints. Currently there is no
     348        specification on how constraints are passed as arguments.
     349
     350        TESTS::
     351
     352            sage: from sage.structure.set_factories_example import XYPairs
     353            sage: XYPairs.add_constraints((3,),((2,), {}))
     354            (3, 2)
     355        """
     356
     357    # TODO ? default policy
     358
     359####################################################
     360#                    Policies                      #
     361####################################################
     362class SetFactoryPolicy(UniqueRepresentation, SageObject):
     363    r"""
     364    Abstract base class for policies.
     365
     366    A policy is a device which is passed to a parent inheriting from
     367    :class:`SetFactoryParent` in order to set-up the element construction
     368    framework.
     369
     370    INPUT:
     371
     372    - ``factory`` -- a :class:`SetFactory`
     373
     374    .. WARNING:: This class is a base class for policies, one should not try
     375       to create instances.
     376    """
     377    def __init__(self, factory):
     378        r"""
     379        TEST::
     380
     381            sage: from sage.structure.set_factories_example import *
     382            sage: from sage.structure.set_factories import *
     383            sage: S = SetFactoryPolicy(XYPairs); S
     384            <class 'sage.structure.set_factories.SetFactoryPolicy'>
     385        """
     386        assert isinstance(factory, SetFactory)
     387        self._factory = factory
     388
     389    def factory(self):
     390        r"""
     391        Return the factory for ``self``
     392
     393        EXAMPLES::
     394
     395            sage: from sage.structure.set_factories_example import *
     396            sage: from sage.structure.set_factories import *
     397            sage: XYPairs._default_policy.factory()
     398            Factory for XY pairs
     399            sage: XYPairs._default_policy.factory() is XYPairs
     400            True
     401
     402        TESTS::
     403
     404            sage: policy = SetFactoryPolicy(XYPairs)
     405            sage: policy.factory()
     406            Factory for XY pairs
     407            sage: SelfParentPolicy(XYPairs, XYPair).factory()
     408            Factory for XY pairs
     409        """
     410        return self._factory
     411
     412    @abstract_method
     413    def category(self, constraints):
     414        r"""
     415        Return the policy category associated to this policy for parent
     416        constructed with the given constraints.
     417
     418        INPUT:
     419
     420        - ``constraints`` -- a set of constraints
     421
     422        OUTPUT:
     423
     424        - a instance of :class:`AbstractPolicyCategory`
     425
     426        This is an abstract method which should be implemented in concrete
     427        policies. It should return an instance of a subclass of
     428        :class:`AbstractPolicyCategory` such as
     429
     430        - :class:`SelfParentPolicyCategory` for usual parent;
     431        - :class:`FacadeParentPolicyCategory` for facade parent (see
     432          :func:`~sage.categories.facade_sets.Facade`);
     433
     434        EXAMPLE::
     435
     436            sage: from sage.structure.set_factories_example import XYPairs
     437            sage: XYPairs._default_policy.category(())
     438            Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     439            sage: XYPairs._default_policy.category((3,))
     440            Policy category with facade parent AllPairs
     441        """
     442
     443
     444class SelfParentPolicy(SetFactoryPolicy):
     445    r"""
     446    Policy where each parent is a standard parent
     447
     448    INPUT:
     449
     450    - ``factory`` -- an instance of :class:`SetFactory`
     451    - ``Element`` -- a subclass of :class:`~.element.Element`
     452
     453    Given a factory ``F`` an a class ``E``, returns a policy for parent ``P``
     454    creating element in class ``E`` and parent ``P`` itself.
     455
     456    EXAMPLES::
     457
     458        sage: from sage.structure.set_factories_example import *
     459        sage: from sage.structure.set_factories import *
     460        sage: pol = SelfParentPolicy(XYPairs, XYPair)
     461        sage: S = Pairs_Y(3, pol)
     462        sage: el = S.an_element()
     463        sage: el.parent() is S
     464        True
     465
     466        sage: class Foo(XYPair): pass
     467        sage: pol = SelfParentPolicy(XYPairs, Foo)
     468        sage: S = Pairs_Y(3, pol)
     469        sage: el = S.an_element()
     470        sage: isinstance(el, Foo)
     471        True
     472    """
     473    def __init__(self, factory, Element):
     474        r"""
     475        TEST::
     476
     477            sage: from sage.structure.set_factories_example import *
     478            sage: from sage.structure.set_factories import *
     479            sage: S = SelfParentPolicy(XYPairs, XYPair); S
     480            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent ``self``
     481            sage: TestSuite(S).run(skip='_test_category')
     482        """
     483        self._Element = Element
     484        SetFactoryPolicy.__init__(self, factory)
     485
     486    def category(self, constraints):
     487        r"""
     488        Return the policy category for given constraints
     489
     490        Return the policy category associated to ``self`` for parent
     491        constructed with the given constraints as per
     492        :meth:`SetFactoryPolicy.category`
     493
     494        INPUT:
     495
     496        - ``constraints`` -- a set of constraints
     497
     498        OUTPUT:
     499
     500        - an instance of :class:`SelfParentPolicyCategory`.
     501
     502        EXAMPLE::
     503
     504            sage: from sage.structure.set_factories_example import *
     505            sage: from sage.structure.set_factories import *
     506            sage: pol = SelfParentPolicy(XYPairs, XYPair)
     507            sage: pol.category(())
     508            Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     509            sage: pol.category((2,))
     510            Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     511        """
     512        return SelfParentPolicyCategory(self._Element)
     513
     514    def _repr_(self):
     515        r"""
     516        TESTS::
     517
     518            sage: from sage.structure.set_factories import SelfParentPolicy
     519            sage: from sage.structure.set_factories_example import *
     520            sage: SelfParentPolicy(XYPairs, XYPair)    # indirect doctest
     521            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent ``self``
     522        """
     523        return "Set factory policy for %s with parent ``self``"%(self._Element)
     524
     525class TopMostParentPolicy(SetFactoryPolicy):
     526    r"""
     527    Policy where the parent of the element is the topmost parent
     528
     529    INPUT:
     530
     531    - ``factory`` -- an instance of :class:`SetFactory`
     532    - ``top_constraints`` -- the empty set of constraints.
     533    - ``Element`` -- a subclass of :class:`~.element.Element`
     534
     535    Given a factory ``F`` an a class ``E``, returns a policy for parent ``P``
     536    creating element in class ``E`` and parent ``factory(top_constraints)``.
     537
     538    EXAMPLES::
     539
     540        sage: from sage.structure.set_factories_example import *
     541        sage: P = XYPairs(); P.policy()
     542        Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     543    """
     544    def __init__(self, factory, top_constraints, Element):
     545        """
     546        TEST::
     547
     548            sage: from sage.structure.set_factories_example import *
     549            sage: from sage.structure.set_factories import *
     550            sage: T = TopMostParentPolicy(XYPairs, (), XYPair); T
     551            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     552            sage: TestSuite(T).run(skip='_test_category')
     553        """
     554        # assert(isinstance(top_constraints, tuple))
     555        self._top_constraints = top_constraints
     556        self._Element = Element
     557        SetFactoryPolicy.__init__(self, factory)
     558
     559    def category(self, constraints):
     560        r"""
     561        Return the policy category for given constraints
     562
     563        Return the policy category associated to ``self`` for parent
     564        constructed with the given constraints as per
     565        :meth:`SetFactoryPolicy.category`
     566
     567        INPUT:
     568
     569        - ``constraints`` -- a set of constraints
     570
     571        OUTPUT:
     572
     573        - the correct policy category. More precisely, if ``constraints`` is
     574          the same as argument ``top_constraints`` passed to the constructor
     575          of ``self``, returns a policy category for normal parent (see class
     576          :class:`SelfParentPolicyCategory`) else returns a policy category
     577          for facade parent (see class :class:`FacadeParentPolicyCategory`).
     578
     579        EXAMPLE::
     580
     581            sage: from sage.structure.set_factories_example import *
     582            sage: from sage.structure.set_factories import *
     583            sage: pol = TopMostParentPolicy(XYPairs, (), XYPair)
     584            sage: pol.category(())
     585            Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     586            sage: pol.category((2,))
     587            Policy category with facade parent AllPairs
     588        """
     589        # assert(isinstance(constraints, tuple))
     590        factory = self._factory
     591        if constraints == self._top_constraints:
     592            return SelfParentPolicyCategory(self._Element)
     593        else:
     594            return FacadeParentPolicyCategory(
     595                factory(*self._top_constraints, policy=self))
     596
     597    def _repr_(self):
     598        r"""
     599        TESTS::
     600
     601            sage: from sage.structure.set_factories_example import *
     602            sage: TopMostParentPolicy(XYPairs, (), XYPair)  # indirect doctest
     603            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     604        """
     605        return "Set factory policy for %s with parent %s[=%s(%s)]"%(
     606            self._Element, self._factory(self._top_constraints),
     607            self._factory, self._top_constraints)
     608
     609
     610class FacadeParentPolicy(SetFactoryPolicy):
     611    r"""
     612    Policy for facade parent
     613
     614    INPUT:
     615
     616    - ``factory`` -- an instance of :class:`SetFactory`
     617    - ``parent`` -- an instance of :class:`Parent`
     618
     619    Given a factory ``F`` an a class ``E``, returns a policy for parent ``P``
     620    creating element as if they were created by ``parent``
     621
     622    EXAMPLES::
     623
     624        sage: from sage.structure.set_factories import *
     625        sage: from sage.structure.set_factories_example import *
     626
     627    We create a custom standard parent ``P``::
     628
     629        sage: selfpolicy = SelfParentPolicy(XYPairs, XYPair)
     630        sage: P = XYPairs(x=2, policy=selfpolicy)
     631        sage: pol = FacadeParentPolicy(XYPairs, P)
     632        sage: P2 = XYPairs(x=2, y=3, policy=pol)
     633        sage: el = P2.an_element()
     634        sage: el.parent() is P
     635        True
     636        sage: type(el) is P.element_class
     637        True
     638
     639    If ``parent`` is itself a facade parent, then transitivity is correctly
     640    applied::
     641
     642        sage: P =  XYPairs()
     643        sage: P2 = XYPairs(x=2)
     644        sage: P2.category()
     645        Join of Category of facade finite enumerated sets and Policy category with facade parent AllPairs
     646        sage: pol = FacadeParentPolicy(XYPairs, P)
     647        sage: P23 = XYPairs(x=2, y=3, policy=pol)
     648        sage: el = P2.an_element()
     649        sage: el.parent() is P
     650        True
     651        sage: type(el) is P.element_class
     652        True
     653    """
     654    def __init__(self, factory, parent):
     655        r"""
     656        TEST::
     657
     658            sage: from sage.structure.set_factories_example import *
     659            sage: from sage.structure.set_factories import *
     660            sage: F = FacadeParentPolicy(XYPairs, XYPairs()); F
     661            Set factory policy for facade parent AllPairs
     662            sage: TestSuite(F).run(skip='_test_category')
     663        """
     664        self._parent_for = parent
     665        SetFactoryPolicy.__init__(self, factory)
     666
     667    def category(self, constraints):
     668        r"""
     669        Return the policy category for given constraints
     670
     671        Return the policy category associated to ``self`` for parent
     672        constructed with the given constraints as per
     673        :meth:`SetFactoryPolicy.category`. Here constraints are ignored.
     674
     675        INPUT:
     676
     677        - ``constraints`` -- a set of constraints (ignored)
     678
     679        OUTPUT:
     680
     681        - an instance of :class:`FacadeParentPolicyCategory`.
     682
     683        EXAMPLE::
     684
     685            sage: from sage.structure.set_factories_example import *
     686            sage: from sage.structure.set_factories import *
     687            sage: F = FacadeParentPolicy(XYPairs, XYPairs())
     688            sage: F.category(())
     689            Policy category with facade parent AllPairs
     690        """
     691        # assert(isinstance(constraints, tuple))
     692        return FacadeParentPolicyCategory(self._parent_for)
     693
     694    def _repr_(self):
     695        r"""
     696        TESTS::
     697
     698            sage: from sage.structure.set_factories import FacadeParentPolicy
     699            sage: from sage.structure.set_factories_example import *
     700            sage: FacadeParentPolicy(XYPairs, XYPairs())  # indirect doctest
     701            Set factory policy for facade parent AllPairs
     702        """
     703        return "Set factory policy for facade parent %s"%(
     704            self._parent_for)
     705
     706####################################################
     707#               Policy Categories                  #
     708####################################################
     709
     710class AbstractPolicyCategory(Category):
     711    r"""
     712    Abstract class for policy category
     713
     714    A policy category is a category for parent inheriting from
     715    :class:`SetFactoryParent` which set-up the element construction
     716    framework.
     717
     718    .. warning:: This class is a base class for policy category, one should
     719       not try to create instances.
     720    """
     721
     722    class ParentMethods:
     723
     724        def policy_category(self):
     725            r"""
     726            Return the policy category of ``self``
     727
     728            TESTS::
     729
     730                sage: from sage.structure.set_factories_example import XYPairs
     731                sage: XYPairs().policy_category()
     732                Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     733                sage: XYPairs(x=3).policy_category()
     734                Policy category with facade parent AllPairs
     735            """
     736            return self._policy.category(self._constraints)
     737
     738
     739        # TODO abstract method parent_for
     740
     741        # QUESTION: Should we call:
     742        #     self._parent_for._element_constructor_
     743        # currently we don't call it directly because:
     744        #  - it may do some extra check we dont want to perform ?
     745        #  - calling directly element_class should be faster
     746        def _element_constructor_(self, *args, **keywords):
     747            r"""
     748            TESTS::
     749
     750                sage: from sage.structure.set_factories_example import XYPairs
     751                sage: XYPairs()((2,3)) # indirect doctest
     752                (2, 3)
     753                sage: XYPairs(x=3)((3,3)) # indirect doctest
     754                (3, 3)
     755                sage: XYPairs(x=3)((2,3)) # indirect doctest
     756                Traceback (most recent call last):
     757                ...
     758                ValueError: Wrong first coordinate
     759
     760                sage: XYPairs(x=3)((2,3), check=False) # Don't do this at home, kids
     761                (2, 3)
     762
     763            .. TODO:: Should we always call check element and let it decide
     764               which check has to be performed ?
     765            """
     766            check =  keywords.get("check", True)
     767            res = self.element_class(self._parent_for, *args, **keywords)
     768            if check:
     769                self.check_element(res, check)
     770            return res
     771
     772
     773class SelfParentPolicyCategory(AbstractPolicyCategory):
     774    r"""
     775    Category implementing the fixed parent policy
     776
     777    A :class:`SetFactoryParent` which is initialized with a policy returning
     778    this category is a normal parent (ie: the parent of its element).
     779
     780    INPUT:
     781
     782    - ``Element`` -- a subclass of :class:`~.element.Element`: the class used
     783      when creating ``self.element_class`` by the category mechanism.
     784
     785    EXAMPLES::
     786
     787        sage: from sage.structure.set_factories_example import XYPairs
     788        sage: S = XYPairs()
     789        sage: S.policy_category()
     790        Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     791        sage: S in S.policy_category()
     792        True
     793    """
     794    def __init__(self, Element):
     795        r"""
     796        TESTS::
     797
     798            sage: from sage.structure.set_factories import SelfParentPolicyCategory
     799            sage: TestSuite(SelfParentPolicyCategory(tuple)).run()
     800        """
     801        self._Element = Element
     802        # nothing to do:
     803        # super(SelfParentPolicyCategory, self).__init__()
     804
     805    def _repr_(self):
     806        r"""
     807        TESTS::
     808
     809            sage: from sage.structure.set_factories import SelfParentPolicyCategory
     810            sage: SelfParentPolicyCategory(tuple) # indirect doctest
     811            Self parent policy category with Element=<type 'tuple'>
     812        """
     813        return "Self parent policy category with Element=%s"%self._Element
     814
     815    def super_categories(self):
     816        r"""
     817        The super category of ``self`` that is ``Sets()``
     818
     819        TESTS::
     820
     821            sage: from sage.structure.set_factories import SelfParentPolicyCategory
     822            sage: SelfParentPolicyCategory(tuple).super_categories()
     823            [Category of sets]
     824        """
     825        return [Sets()]
     826
     827    class ParentMethods(AbstractPolicyCategory.ParentMethods):
     828
     829        @lazy_attribute
     830        def _parent_for(self):
     831            r"""
     832            The actual parent of the element created by ``self``
     833
     834            TESTS::
     835
     836                sage: from sage.structure.set_factories_example import XYPairs
     837                sage: S = XYPairs()
     838                sage: S._parent_for is S
     839                True
     840            """
     841            return self
     842
     843        @lazy_attribute
     844        def Element(self):
     845            r"""
     846            The class of the element of ``self``
     847
     848            TESTS::
     849
     850                sage: from sage.structure.set_factories_example import *
     851                sage: S = XYPairs()
     852                sage: S.Element is XYPair
     853                True
     854                sage: issubclass(S.element_class, XYPair)
     855                True
     856            """
     857            return self.policy_category()._Element
     858
     859
     860class FacadeParentPolicyCategory(AbstractPolicyCategory):
     861    r"""
     862    Category implementing the facade parent policy.
     863
     864    A :class:`SetFactoryParent` which is initialized with a policy returning
     865    this category is a facade parent (ie: the created parent contruct element
     866    as if they where constructed by the given parent).
     867
     868    INPUT:
     869
     870    - ``parent`` -- an instance of :class:`Parent`: the parent used
     871      when creating an element
     872
     873    EXAMPLES::
     874
     875        sage: from sage.structure.set_factories_example import XYPairs
     876        sage: S = XYPairs(x=3)
     877        sage: S.policy_category()
     878        Policy category with facade parent AllPairs
     879        sage: S in S.policy_category()
     880        True
     881    """
     882    def __init__(self, parent):
     883        r"""
     884        TESTS::
     885
     886            sage: from sage.structure.set_factories import FacadeParentPolicyCategory
     887            sage: TestSuite(FacadeParentPolicyCategory(ZZ)).run()
     888        """
     889        self._parent_for = parent
     890        # nothing to do:
     891        # super(FacadeParentPolicyCategory, self).__init__()
     892
     893
     894    def _repr_(self):
     895        r"""
     896        TESTS::
     897
     898            sage: from sage.structure.set_factories import FacadeParentPolicyCategory
     899            sage: FacadeParentPolicyCategory(ZZ) # indirect doctest
     900            Policy category with facade parent Integer Ring
     901        """
     902        return "Policy category with facade parent %s"%self._parent_for
     903
     904    def super_categories(self):
     905        r"""
     906        The super category of ``self`` that is ``Sets().Facade()``
     907
     908        TESTS::
     909
     910            sage: from sage.structure.set_factories import FacadeParentPolicyCategory
     911            sage: FacadeParentPolicyCategory(ZZ).super_categories()
     912            [Category of facade sets]
     913        """
     914        return [Sets().Facade()]
     915
     916    class ParentMethods(AbstractPolicyCategory.ParentMethods):
     917
     918        @lazy_attribute
     919        def _parent_for(self):
     920            r"""
     921            The actual parent of the element created by ``self``
     922
     923            TESTS::
     924
     925                sage: from sage.structure.set_factories_example import XYPairs
     926                sage: S = XYPairs(x=3)
     927                sage: S._parent_for is XYPairs()
     928                True
     929            """
     930            return self.policy_category()._parent_for._parent_for
     931
     932        _facade_for = _parent_for
     933
     934        @lazy_attribute
     935        def element_class(self):
     936            r"""
     937            The class of the element of ``self``
     938
     939            TESTS::
     940
     941                sage: from sage.structure.set_factories_example import *
     942                sage: S = XYPairs(x=3)
     943                sage: S.element_class is XYPairs().element_class
     944                True
     945            """
     946            return self._parent_for.element_class
     947
     948####################################################
     949#                     Parent                       #
     950####################################################
     951
     952class SetFactoryParent(Parent):
     953    r"""
     954    Abstract class for parent belonging to a set factory
     955
     956    INPUT:
     957
     958    - ``constraints`` -- a set of constraints
     959    - ``policy`` -- the policy for element construction
     960    - ``category`` -- the category of the parent (default to ``None``)
     961
     962    Depending on the constraints and the policy, initialize the parent in a
     963    proper category to set up element construction.
     964
     965    EXAMPLES::
     966
     967        sage: from sage.structure.set_factories_example import *
     968        sage: from sage.structure.set_factories import *
     969        sage: P = PairsX_(3, XYPairs._default_policy)
     970        sage: P is XYPairs(3)
     971        True
     972        sage: P.category()
     973        Join of Category of facade finite enumerated sets and Policy category with facade parent AllPairs
     974    """
     975    def __init__(self, constraints, policy, category = None):
     976        r"""
     977        TESTS::
     978
     979            sage: from sage.structure.set_factories import SetFactoryParent
     980            sage: from sage.structure.set_factories_example import XYPairs
     981            sage: isinstance(XYPairs(3), SetFactoryParent)  # indirect doctest
     982            True
     983        """
     984        # assert(isinstance(constraints, tuple))
     985        self._constraints = constraints
     986        assert(isinstance(policy, SetFactoryPolicy))
     987        self._policy = policy
     988        policy_category = policy.category(constraints)
     989        if category is None:
     990            category = policy_category
     991        elif isinstance(category, tuple):
     992            category = category + (policy_category,)
     993        elif isinstance(category, Category):
     994            category = (category, policy_category)
     995        else:
     996            raise TypeError, "%s is not a correct category"%category
     997        # print "category = ", category
     998        Parent.__init__(self, category = category)
     999
     1000    def constraints(self):
     1001        r"""
     1002        Return the constraints for ``self``
     1003
     1004        Currently there is no specification on how constraints are handled.
     1005
     1006        EXAMPLES::
     1007
     1008            sage: from sage.structure.set_factories_example import XYPairs
     1009            sage: XYPairs().constraints()
     1010            ()
     1011            sage: XYPairs(x=3).constraints()
     1012            (3,)
     1013            sage: XYPairs(y=2).constraints()
     1014            (None, 2)
     1015        """
     1016        return self._constraints
     1017
     1018    def policy(self):
     1019        r"""
     1020        Return the policy used when ``self`` was created
     1021
     1022        EXAMPLES::
     1023
     1024            sage: from sage.structure.set_factories_example import XYPairs
     1025            sage: XYPairs().policy()
     1026            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     1027            sage: XYPairs(x=3).policy()
     1028            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     1029        """
     1030        return self._policy
     1031
     1032    def facade_policy(self):
     1033        r"""
     1034        Return the policy for parent facade for ``self``
     1035
     1036        EXAMPLES::
     1037
     1038            sage: from sage.structure.set_factories import *
     1039            sage: from sage.structure.set_factories_example import *
     1040
     1041        We create a custom standard parent ``P``::
     1042
     1043            sage: selfpolicy = SelfParentPolicy(XYPairs, XYPair)
     1044            sage: P = XYPairs(x=2, policy=selfpolicy)
     1045            sage: P.facade_policy()
     1046            Set factory policy for facade parent <class 'sage.structure.set_factories_example.PairsX__with_category'>
     1047
     1048        Now passing ``P.facade_policy()`` creates parent which are facade for
     1049        ``P``::
     1050
     1051            sage: P3 = XYPairs(x=2, y=3, policy=P.facade_policy())
     1052            sage: P3.facade_for() is P
     1053            True
     1054            sage: el = P3.an_element()
     1055            sage: el.parent() is P
     1056            True
     1057        """
     1058        return FacadeParentPolicy(self.factory(), self)
     1059
     1060    def factory(self):
     1061        r"""
     1062        Return the factory which built ``self``
     1063
     1064        EXAMPLES::
     1065
     1066            sage: from sage.structure.set_factories_example import XYPairs
     1067            sage: XYPairs().factory() is XYPairs
     1068            True
     1069            sage: XYPairs(x=3).factory() is XYPairs
     1070            True
     1071        """
     1072        return self._policy.factory()
     1073
     1074    def subset(self, *args, **options):
     1075        r"""
     1076        Return a subset of ``self`` by adding more constraints
     1077
     1078        EXAMPLES::
     1079
     1080            sage: from sage.structure.set_factories_example import XYPairs
     1081            sage: S = XYPairs()
     1082            sage: S3 = S.subset(x=3)
     1083            sage: S3.list()
     1084            [(3, 0), (3, 1), (3, 2), (3, 3), (3, 4)]
     1085
     1086        TESTS::
     1087
     1088            sage: S3 is XYPairs(3)
     1089            True
     1090            sage: S3 is XYPairs(x=3)
     1091            True
     1092        """
     1093        factory = self.factory()
     1094        constr = factory.add_constraints(self._constraints,
     1095                                         (args, options))
     1096        return factory(*constr, policy = self._policy, **options)
     1097
     1098    def _test_subset(self, **options):
     1099        r"""
     1100        Tests that subsets with no extra parameters returns ``self``
     1101
     1102        Currently only test that one gets the same parent is no more
     1103        constraints are added.
     1104
     1105        .. TODO::
     1106
     1107            Straighten the test when handling of constraints will be
     1108            specified.
     1109
     1110        EXAMPLES::
     1111
     1112            sage: from sage.structure.set_factories_example import XYPairs
     1113            sage: S = XYPairs()
     1114            sage: S._test_subset()
     1115        """
     1116        #TODO verifie avec self.constraints
     1117        tester = self._tester(**options)
     1118        tester.assertTrue(self.subset() is self)
     1119
     1120    @abstract_method
     1121    def check_element(self, x, check):
     1122        r"""
     1123        Check that ``x`` verifies the constraints of ``self``
     1124
     1125        INPUT:
     1126
     1127        - ``x`` -- an instance of ``self.element_class``.
     1128
     1129        - ``check`` -- the level of checking to be performed (usually a
     1130          boolean).
     1131
     1132        This method may assume that ``x`` was properly constructed by ``self``
     1133        or a possible super-set of ``self`` for which ``self`` is a facade. It
     1134        should return nothing is ``x`` verifies the constraints and raise a
     1135        :exc:`~exceptions.ValueError` explaining which constraints ``x``
     1136        fails otherwise.
     1137
     1138        The method should accept an extra parameter check specifying which
     1139        level of check should be performed. It will only be called when
     1140        ``bool(check)`` evaluates to ``True``.
     1141
     1142        .. TODO:: Should we always call check element and let it decide which
     1143           check has to be performed ?
     1144
     1145        EXAMPLES::
     1146
     1147            sage: from sage.structure.set_factories_example import XYPairs
     1148            sage: S = XYPairs()
     1149            sage: el = S((2,3))
     1150            sage: S.check_element(el, True)
     1151            sage: XYPairs(x=2).check_element(el, True)
     1152            sage: XYPairs(x=3).check_element(el, True)
     1153            Traceback (most recent call last):
     1154            ...
     1155            ValueError: Wrong first coordinate
     1156            sage: XYPairs(y=4).check_element(el, True)
     1157            Traceback (most recent call last):
     1158            ...
     1159            ValueError: Wrong second coordinate
     1160        """
     1161
     1162    def __contains__(self, x):
     1163        r"""
     1164        Default implementation for ``__contains__``:
     1165
     1166        INPUT::
     1167
     1168        - ``x`` -- any object
     1169
     1170        Check for class, parent and calls ``self.check_element(x)``
     1171
     1172        TESTS::
     1173
     1174            sage: from sage.structure.set_factories_example import XYPairs
     1175            sage: S = XYPairs()
     1176            sage: el = S((2,3))
     1177            sage: el in S
     1178            True
     1179            sage: el in XYPairs(x=2)
     1180            True
     1181            sage: el in XYPairs(x=3)
     1182            False
     1183            sage: el in XYPairs(y=4)
     1184            False
     1185        """
     1186        if (isinstance(x, self.element_class) and
     1187            x.parent() == self._parent_for):
     1188            try:
     1189                self.check_element(x, True)
     1190            except ValueError:
     1191                return False
     1192            else:
     1193                return True
     1194        else:
     1195            return False
     1196
     1197    def __call__(self, *args, **keywords):
     1198        r"""
     1199        TESTS::
     1200
     1201            sage: from sage.structure.set_factories_example import XYPairs
     1202            sage: S = XYPairs()
     1203            sage: el = S((2,3)); el
     1204            (2, 3)
     1205            sage: S(el) is el
     1206            True
     1207
     1208            sage: XYPairs(x=3)((2,3))
     1209            Traceback (most recent call last):
     1210            ...
     1211            ValueError: Wrong first coordinate
     1212
     1213            sage: XYPairs(x=3)(el)
     1214            Traceback (most recent call last):
     1215            ...
     1216            ValueError: Wrong first coordinate
     1217
     1218        .. TODO:: Should we always call check element and let it decide which
     1219           check has to be performed ?
     1220
     1221        """
     1222        # Ensure idempotence of element construction
     1223        if (len(args) == 1 and
     1224            isinstance(args[0], self.element_class) and
     1225            args[0].parent() == self._parent_for):
     1226            check = keywords.get("check", True)
     1227            if check:
     1228                self.check_element(args[0], check)
     1229            return args[0]
     1230        else:
     1231            return Parent.__call__(self, *args, **keywords)
     1232
     1233
  • new file sage/structure/set_factories_example.py

    diff --git a/sage/structure/set_factories_example.py b/sage/structure/set_factories_example.py
    new file mode 100644
    - +  
     1"""
     2Examples of set factories
     3=========================
     4
     5The goal of this module is to exemplify the use of set factories. Note that
     6the written code is intentionally kept minimal, many thing and in particular
     7several iterator could be written in a more efficient ways.
     8
     9.. SEEALSO:: :mod:`.set_factories` for an introduction, example of usage and
     10    the specification of set factories.
     11"""
     12#*****************************************************************************
     13#  Copyright (C) 2012 Florent Hivert <florent.hivert at lri.fr>
     14#
     15#  Distributed under the terms of the GNU General Public License (GPL)
     16#                  http://www.gnu.org/licenses/
     17#*****************************************************************************
     18
     19from sage.structure.unique_representation import UniqueRepresentation
     20from sage.structure.element_wrapper import ElementWrapper
     21from sage.structure.set_factories import (
     22    SetFactory, SetFactoryParent, TopMostParentPolicy)
     23from sage.sets.all import (Family, DisjointUnionEnumeratedSets)
     24from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     25from sage.rings.integer import Integer
     26from sage.misc.lazy_attribute import lazy_attribute
     27
     28MAX = 5
     29
     30class XYPair(ElementWrapper):
     31    r"""
     32    A class for Element `(x,y)` with `x` and `y` in `\{0,1,2,3,4\}`
     33
     34    EXAMPLES::
     35
     36        sage: from sage.structure.set_factories_example import *
     37        sage: p = XYPair(Parent(), (0,1)); p
     38        (0, 1)
     39        sage: p = XYPair(Parent(), (0,8))
     40        Traceback (most recent call last):
     41        ...
     42        ValueError: numbers must be in range(5)
     43    """
     44    def __init__(self, parent, value, check=True):
     45        """
     46        TESTS::
     47
     48            sage: from sage.structure.set_factories_example import *
     49            sage: P = XYPairs(); p = P.list()[0]
     50            sage: TestSuite(p).run()
     51        """
     52        if check:
     53            if not isinstance(value, tuple):
     54                raise ValueError, "Value %s must be a tuple"%value
     55            if len(value) != 2:
     56                raise ValueError, "Value must be of length 2"
     57            if not all(int(x) in range(MAX) for x in value):
     58                raise ValueError, "numbers must be in range(%s)"%MAX
     59        ElementWrapper.__init__(self, value, parent)
     60
     61class AllPairs(SetFactoryParent, DisjointUnionEnumeratedSets):
     62    r"""
     63    This parent show how one can use set factories together with
     64    :class:`DisjointUnionEnumeratedSets`
     65
     66    TESTS::
     67
     68        sage: from sage.structure.set_factories_example import XYPairs
     69        sage: P = XYPairs(); P.list()
     70        [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4)]
     71    """
     72    def __init__(self, policy):
     73        r"""
     74        TESTS::
     75
     76            sage: from sage.structure.set_factories_example import XYPairs
     77            sage: TestSuite(XYPairs()).run()
     78        """
     79        SetFactoryParent.__init__(self, (), policy,
     80            category = FiniteEnumeratedSets())
     81        DisjointUnionEnumeratedSets.__init__(
     82            self, Family(range(MAX), self._single_pair),
     83            facade=True, keepkey = False,
     84            category = FiniteEnumeratedSets())
     85
     86    def _single_pair(self, letter):
     87        r"""
     88        TESTS::
     89
     90            sage: from sage.structure.set_factories_example import XYPairs
     91            sage: XYPairs()._single_pair(1)
     92            Disjoint union of Finite family {0: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>, 1: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>, 2: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>, 3: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>, 4: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>}
     93        """
     94        return Pairs_Y(letter, policy=self.facade_policy())
     95
     96    def _repr_(self):
     97        r"""
     98        TESTS::
     99
     100            sage: from sage.structure.set_factories_example import XYPairs
     101            sage: XYPairs()   # indirect doctest
     102            AllPairs
     103        """
     104        return "AllPairs"
     105
     106    def check_element(self, el, check):
     107        r"""
     108        TESTS::
     109
     110            sage: from sage.structure.set_factories_example import XYPairs
     111            sage: P = XYPairs()
     112            sage: P.check_element(P.an_element(), True)
     113            sage: XYPairs()((7, 0))  # indirect doctest
     114            Traceback (most recent call last):
     115            ...
     116            ValueError: numbers must be in range(5)
     117        """
     118        pass
     119
     120class PairsX_(SetFactoryParent, UniqueRepresentation):
     121    r"""
     122    The set of pair `(x, 0), (x, 1), ..., (x, 4)`
     123
     124    TESTS::
     125
     126        sage: from sage.structure.set_factories_example import XYPairs
     127        sage: P = XYPairs(0); P.list()
     128        [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)]
     129    """
     130    def __init__(self, x, policy):
     131        r"""
     132        TESTS::
     133
     134            sage: from sage.structure.set_factories_example import XYPairs
     135            sage: TestSuite(XYPairs(0)).run()
     136        """
     137        self._x = x
     138        SetFactoryParent.__init__(self, (x,), policy,
     139            category = FiniteEnumeratedSets())
     140
     141    def an_element(self):
     142        r"""
     143        TESTS::
     144
     145            sage: from sage.structure.set_factories_example import XYPairs
     146            sage: P = XYPairs(x=0); P.an_element()
     147            (0, 0)
     148        """
     149        return self._element_constructor_((self._x, 0), check=False)
     150
     151    def check_element(self, el, check):
     152        r"""
     153        TESTS::
     154
     155            sage: from sage.structure.set_factories_example import XYPairs
     156            sage: P = XYPairs(x=1)
     157            sage: P.check_element(P.an_element(), True)
     158            sage: XYPairs(x=1)((0, 0))  # indirect doctest
     159            Traceback (most recent call last):
     160            ...
     161            ValueError: Wrong first coordinate
     162        """
     163        (x, y) = el.value
     164        if x != self._x:
     165            raise ValueError, "Wrong first coordinate"
     166
     167    def __iter__(self):
     168        r"""
     169        TESTS::
     170
     171            sage: from sage.structure.set_factories_example import XYPairs
     172            sage: list(XYPairs(x=1))
     173            [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4)]
     174        """
     175        for i in range(MAX):
     176            yield self._element_constructor_((self._x, i), check=False)
     177
     178
     179
     180class Pairs_Y(SetFactoryParent, DisjointUnionEnumeratedSets):
     181    r"""
     182    The set of pair `(0, y), (1, y), ..., (4, y)`
     183
     184    TESTS::
     185
     186        sage: from sage.structure.set_factories_example import XYPairs
     187        sage: P = XYPairs(y=1); P.list()
     188        [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)]
     189    """
     190    def __init__(self, y, policy):
     191        r"""
     192        TESTS::
     193
     194            sage: from sage.structure.set_factories_example import XYPairs
     195            sage: TestSuite(XYPairs(y=1)).run()
     196        """
     197        self._y = y
     198        SetFactoryParent.__init__(self, (None, y), policy,
     199            category = FiniteEnumeratedSets())
     200        DisjointUnionEnumeratedSets.__init__(
     201            self, Family(range(MAX), self._single_pair),
     202            facade=True, keepkey = False,
     203            category = FiniteEnumeratedSets()) # TODO remove and fix disjoint union.
     204
     205    def an_element(self):
     206        r"""
     207        TESTS::
     208
     209            sage: from sage.structure.set_factories_example import XYPairs
     210            sage: XYPairs(y=1).an_element()
     211            (0, 1)
     212        """
     213        return self._element_constructor_((0, self._y), check=False)
     214
     215    def _single_pair(self, letter):
     216        r"""
     217        TESTS::
     218
     219            sage: from sage.structure.set_factories_example import XYPairs
     220            sage: XYPairs(y=1)._single_pair(0)
     221            <class 'sage.structure.set_factories_example.SingletonPair_with_category'>
     222        """
     223        return SingletonPair(letter, self._y, policy=self.facade_policy())
     224
     225    def check_element(self, el, check):
     226        r"""
     227        TESTS::
     228
     229            sage: from sage.structure.set_factories_example import XYPairs
     230            sage: P = XYPairs(y=1)
     231            sage: P.check_element(P.an_element(), True)
     232            sage: XYPairs(y=1)((1, 0))  # indirect doctest
     233            Traceback (most recent call last):
     234            ...
     235            ValueError: Wrong second coordinate
     236        """
     237        (x, y) = el.value
     238        if y != self._y:
     239            raise ValueError, "Wrong second coordinate"
     240
     241
     242class SingletonPair(SetFactoryParent, UniqueRepresentation):
     243    r"""
     244    TESTS::
     245
     246        sage: from sage.structure.set_factories_example import XYPairs
     247        sage: P = XYPairs(0,1); P.list()
     248        [(0, 1)]
     249    """
     250    def __init__(self, x, y, policy):
     251        r"""
     252        TESTS::
     253
     254            sage: from sage.structure.set_factories_example import XYPairs
     255            sage: TestSuite(XYPairs(0,1)).run()
     256        """
     257        self._xy = (x, y)
     258        SetFactoryParent.__init__(self, (x, y), policy,
     259            category = FiniteEnumeratedSets())
     260
     261    def check_element(self, el, check):
     262        r"""
     263        TESTS::
     264
     265            sage: from sage.structure.set_factories_example import XYPairs
     266            sage: XYPairs(0,1).check_element(XYPairs()((0,1)), True)
     267            sage: XYPairs(0,1).check_element(XYPairs()((1,0)), True)
     268            Traceback (most recent call last):
     269            ...
     270            ValueError: Wrong coordinate
     271            sage: XYPairs(0,1)((1,1))
     272            Traceback (most recent call last):
     273            ...
     274            ValueError: Wrong coordinate
     275        """
     276        if el.value != self._xy:
     277            raise ValueError, "Wrong coordinate"
     278
     279    def __iter__(self):
     280        r"""
     281        TESTS::
     282
     283            sage: from sage.structure.set_factories_example import XYPairs
     284            sage: list(XYPairs(0,1))
     285            [(0, 1)]
     286        """
     287        yield self._element_constructor_(self._xy, check=False)
     288
     289
     290class XYPairsFactory(SetFactory):
     291    r"""
     292    An example of factory for sets of pairs of integers
     293
     294    .. SEEALSO:: :mod:`.set_factories` for an introduction to factories.
     295    """
     296    def __call__(self, x=None, y=None, policy=None):
     297        r"""
     298        Construct the subset from constraints
     299
     300        Consider the set `S` of couple `(x,y)` with `x` and `y` in
     301        `I:=\{0,1,2,3,4\}`. Returns the subsets of element of `S` satisfying
     302        some constraints.
     303
     304        INPUT:
     305
     306        - ``x=a`` -- where ``a`` is an integer (default to ``None``).
     307        - ``y=b`` -- where ``b`` is an integer (default to ``None``).
     308        - ``policy`` -- the policy passed to the created set.
     309
     310        .. SEEALSO:: :class:`.set_factories.SetFactoryPolicy`
     311
     312        EXAMPLES::
     313
     314            sage: from sage.structure.set_factories_example import XYPairsFactory
     315            sage: XYPairs = XYPairsFactory()
     316            sage: P = XYPairs(); P.list()
     317            [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4)]
     318
     319        .. note:: This function is actually the ``__call__`` method of
     320                  :class:`XYPairsFactory`.
     321        """
     322        if policy is None:
     323            policy = self._default_policy
     324
     325        if isinstance(x, (Integer, int)):
     326            if isinstance(y, (Integer, int)):
     327                return SingletonPair(x, y, policy)
     328            else:
     329                return PairsX_(x, policy)
     330        elif isinstance(y, (Integer, int)):
     331                return Pairs_Y(y, policy)
     332        else:
     333            return AllPairs(policy)
     334
     335    def add_constraints(self, cons, (args, opts)):
     336        r"""
     337        Add constraints to the set ``cons`` as per :meth:`SetFactory.add_constraints <.set_factories.SetFactory.add_constraints>`
     338
     339        This is a very crude implementation which ignore optional arguments.
     340        They will be handled directly by the factory :func:`XYPair`
     341
     342        EXAMPLE::
     343
     344            sage: from sage.structure.set_factories_example import XYPairs
     345            sage: XYPairs.add_constraints((3,), ((2,), {}))
     346            (3, 2)
     347            sage: XYPairs.add_constraints((), ((2,), {}))
     348            (2,)
     349        """
     350        return cons+args
     351
     352    @lazy_attribute
     353    def _default_policy(self):
     354        r"""
     355        TESTS::
     356
     357            sage: from sage.structure.set_factories_example import XYPairsFactory
     358            sage: XYPairs = XYPairsFactory()
     359            sage: XYPairs._default_policy
     360            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     361        """
     362        return TopMostParentPolicy(self, (), XYPair)
     363
     364    def _repr_(self):
     365        """
     366        TESTS::
     367
     368            sage: from sage.structure.set_factories_example import XYPairs
     369            sage: XYPairs   # indirect doctest
     370            Factory for XY pairs
     371        """
     372        return "Factory for XY pairs"
     373
     374XYPairs = XYPairsFactory()
     375XYPairs.__doc__ = XYPairsFactory.__call__.__doc__