From 77279f919b0f8e8317e427b13fad8b6dfac48183 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Sun, 6 Apr 2014 22:47:29 +0000 Subject: [PATCH 01/11] # Mon Apr 07 00:47:29 2014 +0200 # Node ID 93d0bacd7d2ebcb376c37f2a292c0fe1d872f57d # Parent e0a9dce709563150fcb46f3bf578b3746390d414 imported patch trac_10194-factories_policy-fh.patch --- src/doc/en/reference/structure/index.rst | 2 + src/sage/misc/sagedoc.py | 3 + src/sage/structure/set_factories.py | 1030 +++++++++++++++++++ src/sage/structure/set_factories_example.py | 407 ++++++++ 4 files changed, 1442 insertions(+) create mode 100644 src/sage/structure/set_factories.py create mode 100644 src/sage/structure/set_factories_example.py diff --git a/src/doc/en/reference/structure/index.rst b/src/doc/en/reference/structure/index.rst index 4c448524942..c603b4a5c3e 100644 --- a/src/doc/en/reference/structure/index.rst +++ b/src/doc/en/reference/structure/index.rst @@ -22,6 +22,8 @@ Basic Structures sage/structure/sequence sage/structure/element_wrapper sage/structure/global_options + sage/structure/set_factories + sage/structure/set_factories_example sage/sets/cartesian_product sage/sets/family diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 65f964c56aa..3d18c0873a7 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -55,6 +55,9 @@ ('\\cdot', ' *'), (' \\times', ' x'), ('\\times', ' x'), + ('\\quad', ' '), + ('\\qquad', ' '), + ('\\mid', '|'), ('\\backslash','\\'), ('\\mapsto', ' |--> '), ] diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py new file mode 100644 index 00000000000..64fd8b0ccd4 --- /dev/null +++ b/src/sage/structure/set_factories.py @@ -0,0 +1,1030 @@ +r""" +Set factories +============= + + +A *set factory* `F` is a device for constructing :class:`Parent`s `P` +that model subsets of a big set `S`. Typically, each such parent is +constructed as the subset of `S` of all elements satisfying a certain +collection of constraints `cons`. In such a hierarchy of subsets, one +needs easy and flexible control on how elements are construted. For +example, one may want to construct the elements of `P` in some +subclass of the class of the elements of `S`. On other occasions, one +also often needs `P` to be a facade parent, whose elements are +represented as elements of `S` (see +:func:`~sage.categories.facade_sets.Facade`). + +The role of a set factory is twofold: + +- *manage a database* of constructors for the different parents `P = F(cons)` + depending on the various kinds of constraints `cons`. Note: currently there + is no real support for that. We are gathering use cases before fixing the + interface. + +- ensure that the elements `e = P(...)` created by the different parents + follows a consistent policy concerning their *class and parent*. + +.. RUBRIC:: Basic usage: constructing parents through a factory + +The file :mod:`sage.structure.set_factories_example` shows an example of a +:class:`SetFactory` together with typical implementation. Note that the +written code is intentionnally kept minimal, many thing and in particular +several iterator could be written in a more efficient ways. + +Consider the set `S` of couple `(x,y)` with `x` and `y` in `I:=\{0,1,2,3,4\}`. +We represent the element of `S` as 2-elements tuple, wrapped in a class +:class:`~.set_factories_example.XYPair` deriving from :class:`ElementWrapper`. +You can create a :class:`~.set_factories_example.XYPair` with any +:class:`Parent`:: + + sage: from sage.structure.set_factories import * + sage: from sage.structure.set_factories_example import * + sage: p = XYPair(Parent(), (0,1)); p + (0, 1) + +Now, given `(a, b)\in S` we want to consider the following subsets of +`S` + +.. MATH:: + + S_a := \{(x,y) \in S \mid x = a\},\qquad + S^b := \{(x,y) \in S \mid y = b\},\qquad + S_a^b := \{(x,y) \in S \mid x = a, y = b\}. + +The constraints considered here are admittedly trivial. In a realistic +examples, there would be much more of them. And for some sets of constraints +no good enumeration algorithms would be known. + +In Sage, those sets are constructed passing the constraints to the factory. We +first create the set with no constraints at all:: + + sage: XYPairs + Factory for XY pairs + sage: S = XYPairs(); S.list() + [(0, 0), (1, 0), ..., (4, 0), (0, 1), (1, 1), ..., (3, 4), (4, 4)] + sage: S.cardinality() + 25 + +Let's construct `S_2`, `S^3` and `S_2^3`:: + + sage: Sx2 = XYPairs(x=2); Sx2.list() + [(2, 0), (2, 1), (2, 2), (2, 3), (2, 4)] + sage: Sy3 = XYPairs(y=3); Sy3.list() + [(0, 3), (1, 3), (2, 3), (3, 3), (4, 3)] + sage: S23 = XYPairs(x=2, y=3); S23.list() + [(2, 3)] + +Set factories provide an alternative way to build subsets of an already +constructed set: each set constructed by a factory has a method +:meth:`~SetFactoryParent.subset` which accept new constraints. Set +constructed by the factory or the :meth:`~SetFactoryParent.subset` methods are +identical:: + + sage: Sx2s = S.subset(x=2); Sx2 is Sx2s + True + sage: Sx2.subset(y=3) is S23 + True + +It is not possible to change an already given constraints:: + + sage: S23.subset(y=5) + Traceback (most recent call last): + ... + TypeError: __call__() got multiple values for keyword argument 'y' + +.. RUBRIC:: Constructing custom elements: policies + +We now come to the point of factories: constructing custom elements. By +default, the writer of :func:`~.set_factories_example.XYPairs` decided that +the parent ``Sx2``, ``Sy3`` and ``S23`` are facade for parent ``S``. This +means that each elements constructed by those subsets behaves as if they where +directly constructed by ``S`` istelf:: + + sage: Sx2.an_element().parent() + AllPairs + sage: el = Sx2.an_element() + sage: el.parent() is S + True + sage: type(el) is S.element_class + True + +This is not always desirable. The device which decides how to construct an +element is called a *policy* (see :class:`SetFactoryPolicy`). Each factory +should have a default policy. Here is the policy of +:func:`~.set_factories_example.XYPairs`:: + + sage: XYPairs._default_policy + Set factory policy for with parent AllPairs[=Factory for XY pairs(())] + +This means that with the current policy, the parent builds element with class +``XYPair`` and parent ``AllPairs`` which is itself constructed by calling the +factory :func:`~.set_factories_example.XYPairs` with constraints ``()``. There +is a lot of flexibility to change that. We now illustrate how to make two +different choices. + +1 - In a first use case, we want to add some method to the constructed +elements. As illustration, we add here a new method ``sum`` which returns +`x+y`. We therefore inherit from :class:`~.set_factories_example.XYPair`:: + + sage: class NewXYPair(XYPair): + ... def sum(self): + ... return sum(self.value) + sage: el = NewXYPair(Parent(), (2,3)) + sage: el.sum() + 5 + +We now want to have subsets generating those new elements while still having a +single real parent (the one with no constraints) for each element. The +corresponding policy is called :class:`TopMostParentPolicy`. It takes tree +parameters: + +- the factory; +- the parameters for void constraints; +- the class used for elements. + +Calling the factory with this policy returns a new set which builds its +elements with the new policy:: + + sage: newpolicy = TopMostParentPolicy(XYPairs, (), NewXYPair) + sage: newS = XYPairs(policy = newpolicy) + sage: el = newS.an_element(); el + (0, 0) + sage: el.sum() + 0 + sage: el.parent() is newS + True + +Subsets now inherits the policy:: + + sage: newS2 = newS.subset(x=2) + sage: el2 = newS2.an_element(); el2 + (2, 0) + sage: el2.sum() + 2 + sage: el2.parent() is newS + True + +2 - In a second use case, we want the elements to remember which parent +created them. The corresponding policy is called +:class:`SelfParentPolicy`. It takes only two parameters: + +- the factory; +- the class used for elements. + +Here is an examples:: + + sage: selfpolicy = SelfParentPolicy(XYPairs, NewXYPair) + sage: selfS = XYPairs(policy = selfpolicy) + sage: el = selfS.an_element(); + sage: el.parent() is selfS + True + +Now subsets are the parent of the element they created:: + + sage: selfS2 = selfS.subset(x=2) + sage: el2 = selfS2.an_element() + sage: el2.parent() is newS + False + sage: el2.parent() is selfS2 + True + +Here are the currently implemented policies: + +- :class:`FacadeParentPolicy`: reuse an existing parent together with + its element_class + +- :class:`TopMostParentPolicy`: use a parent created by the factory itself and + provide a class ``Element`` for it. In this case, we need to specify for + which sets of constraints the constructed parent needs to be provided with + a class ``Element``. + +- :class:`SelfParentPolicy`: provide systematically Element and + element_class and ensure that the parent is ``self``. + +.. TODO:: Generalize :class:`TopMostParentPolicy` to be able to have several + topmost parent. + +.. RUBRIC:: Technicalities: how policies inform parents + +Parent built from factories should inherits from +:class:`SetFactoryParent`. This class provide a few methods related to +factories and policies. The ``__init__`` method of :class:`SetFactoryParent` +must be provided with the set of constraints and the policy. A parent built +from a factory must creates element trough a call to the method +``_element_constructor_``. The current way policies inform parents how to +builds their elements is by setting a few attributes. So the class must accept +attribute adding. The precise details of which attribute are set may be subject to change in the future. + +.. RUBRIC:: How to write a set factory + +.. SEEALSO:: :mod:`.set_factories_example` for an example of a factory. + +Here are the specifications: + +A parent built from a factory should + +- *inherit* from :class:`SetFactoryParent`. It should accept a ``policy`` + argument and pass it verbatim to the ``__init__`` method of + :class:`SetFactoryParent` together with the set of constraints; + +- *create its elements* trough calls to the method ``_element_constructor_``; + +- *define a method* :class:`SetFactoryParent.check_element` which checks if a + built element indeed belongs to it. The method should accept an extra + keyword parameter called ``check`` specifying which level of check should be + performed. It will only be called when ``bool(check)`` evaluates to + ``True``. + +The constructor of the elements of a parent from a factory should: + +- receive the parent as first mandatory argument; + +- accept an extra optional keyword parameter called ``check`` which is meant + to tell if the input must be checked or not. The precise meaning of + ``check`` is intensionally left vague. The only intend is that if + ``bool(check)`` evaluates to ``False`` no check is performed at all. + +A factory should + +- *define a method* ``__call__`` which is responsible for calling the + appropriate parent constructor given the constraints; + +- *define a method* overloading :meth:`SetFactory.add_constraints` which is + responsible of computing the union of two sets of constraints; + +- *optionally* define an attribute ``_default_policy`` used by call to pass to + the parent. + +.. TODO:: There is currently no support for dealing with sets of + constraints. The set factory and the parents must cooperate to + consistently handle them. More support, together with a generic mechanism + to select the appropriate parent class from the constraints, will be added + shortly to sage, as soon as we have gathered sufficiently enough + use-cases. + +AUTHORS: + +- Florent Hivert (2011-2012): initial revision +""" +#***************************************************************************** +# Copyright (C) 2012 Florent Hivert +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +from sage.structure.sage_object import SageObject +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.category import Category +from sage.categories.sets_cat import Sets +from sage.misc.abstract_method import abstract_method +from sage.misc.lazy_attribute import lazy_attribute + +#################################################### +# Factories # +#################################################### + +class SetFactory(UniqueRepresentation, SageObject): + r""" + This class is currently just a stub we will be using to add more + structures on factories. + """ + @abstract_method + def __call__(self, *constraints, **consdict): + r""" + Construct the parent associated with the constraints + + Should return a :class:`Parent`. + + Currently there is no specification on how constraints are passed as + arguments. + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs() + AllPairs + sage: XYPairs(3) + {(3, b) | b in range(5)} + + sage: XYPairs(x=3) + {(3, b) | b in range(5)} + + """ + + @abstract_method + def add_constraints(self, cons, *args, **opts): + r""" + Add constraints to the set cons + + Should return a set of constraints. Currently there is no + specification on how constraints are passed as arguments. + + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs.add_constraints((3,),((2,), {})) + (3, 2) + """ + + # TODO ? default policy + +#################################################### +# Policies # +#################################################### +class SetFactoryPolicy(UniqueRepresentation, SageObject): + r""" + Abstract base class for policies. + + A policy is a device which is passed to a parent inheriting from + :class:`SetFactoryParent` in order to set-up the element construction + framework. + + INPUT: + + - ``factory`` -- a :class:`SetFactory` + + .. WARNING:: This class is a base class for policies, one should not try + to create instances. + """ + def __init__(self, factory): + r""" + TEST:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: S = SetFactoryPolicy(XYPairs); S + + """ + assert isinstance(factory, SetFactory) + self._factory = factory + + def factory(self): + r""" + Return the factory for ``self`` + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: XYPairs._default_policy.factory() + Factory for XY pairs + sage: XYPairs._default_policy.factory() is XYPairs + True + + TESTS:: + + sage: policy = SetFactoryPolicy(XYPairs) + sage: policy.factory() + Factory for XY pairs + sage: SelfParentPolicy(XYPairs, XYPair).factory() + Factory for XY pairs + """ + return self._factory + + def _self_element_constructor_attributes(self, Element): + r""" + Element Constructor Attributes for non facade parent + + The list of attribute with must be set during the init of a non facade + parent with factory. + + INPUT:: + + - ``Element`` -- the class used for the elements + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs, XYPair + sage: pol = XYPairs._default_policy + sage: pol._self_element_constructor_attributes(XYPair) + {'_parent_for': 'self', 'Element': } + """ + return {'_parent_for' : "self", + 'Element' : Element} + + def _facade_element_constructor_attributes(self, parent): + r""" + Element Constructor Attributes for facade parent + + The list of attribute with must be set during the init of a facade + parent with factory. + + INPUT:: + + - ``parent`` -- the actual parent for the elements + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs, XYPair + sage: pol = XYPairs._default_policy + sage: pol._facade_element_constructor_attributes(XYPairs()) + {'element_class': , '_facade_for': AllPairs, '_parent_for': AllPairs} + """ + return {'_parent_for' : parent, + '_facade_for' : parent, + 'element_class' : parent.element_class} + + @abstract_method + def _element_constructor_attributes(self, constraints): + r""" + Element constructor attributes + + INPUT: + + - ``constraints`` -- a bunch of constraints + + Should returns the attributes that are prerequisite for element + construction. This is coordinated with + :meth:`SetFactoryParent._element_constructor_`. Currently to standard + attributes are provided in + :meth:`_facade_element_constructor_attributes` and + :meth:`_self_element_constructor_attributes`. You should returns to + one needed depending on the given constraints. + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs, XYPair + sage: pol = XYPairs._default_policy + sage: pol._element_constructor_attributes(()) + {'_parent_for': 'self', 'Element': } + sage: pol._element_constructor_attributes((1)) + {'element_class': , '_facade_for': AllPairs, '_parent_for': AllPairs} + """ + +class SelfParentPolicy(SetFactoryPolicy): + r""" + Policy where each parent is a standard parent + + INPUT: + + - ``factory`` -- an instance of :class:`SetFactory` + - ``Element`` -- a subclass of :class:`~.element.Element` + + Given a factory ``F`` an a class ``E``, returns a policy for parent ``P`` + creating element in class ``E`` and parent ``P`` itself. + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: pol = SelfParentPolicy(XYPairs, XYPair) + sage: S = Pairs_Y(3, pol) + sage: el = S.an_element() + sage: el.parent() is S + True + + sage: class Foo(XYPair): pass + sage: pol = SelfParentPolicy(XYPairs, Foo) + sage: S = Pairs_Y(3, pol) + sage: el = S.an_element() + sage: isinstance(el, Foo) + True + """ + def __init__(self, factory, Element): + r""" + TEST:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: S = SelfParentPolicy(XYPairs, XYPair); S + Set factory policy for with parent ``self`` + sage: TestSuite(S).run(skip='_test_category') + """ + self._Element = Element + SetFactoryPolicy.__init__(self, factory) + + def _element_constructor_attributes(self, constraints): + r""" + Returns the element constructor attributes as per + :meth:`SetFactoryPolicy._element_constructor_attributes` + + INPUT: + + - ``constraints`` -- a bunch of constraints + + TESTS:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: pol = SelfParentPolicy(XYPairs, XYPair) + sage: pol._element_constructor_attributes(()) + {'_parent_for': 'self', 'Element': } + """ + return self._self_element_constructor_attributes(self._Element) + + def _repr_(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories import SelfParentPolicy + sage: from sage.structure.set_factories_example import * + sage: SelfParentPolicy(XYPairs, XYPair) # indirect doctest + Set factory policy for with parent ``self`` + """ + return "Set factory policy for %s with parent ``self``"%(self._Element) + +class TopMostParentPolicy(SetFactoryPolicy): + r""" + Policy where the parent of the element is the topmost parent + + INPUT: + + - ``factory`` -- an instance of :class:`SetFactory` + - ``top_constraints`` -- the empty set of constraints. + - ``Element`` -- a subclass of :class:`~.element.Element` + + Given a factory ``F`` and a class ``E``, returns a policy for parent ``P`` + creating element in class ``E`` and parent ``factory(top_constraints)``. + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import * + sage: P = XYPairs(); P.policy() + Set factory policy for with parent AllPairs[=Factory for XY pairs(())] + """ + def __init__(self, factory, top_constraints, Element): + """ + TEST:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: T = TopMostParentPolicy(XYPairs, (), XYPair); T + Set factory policy for with parent AllPairs[=Factory for XY pairs(())] + sage: TestSuite(T).run(skip='_test_category') + """ + # assert(isinstance(top_constraints, tuple)) + self._top_constraints = top_constraints + self._Element = Element + SetFactoryPolicy.__init__(self, factory) + + def _element_constructor_attributes(self, constraints): + r""" + Returns the element constructor attributes as per + :meth:`SetFactoryPolicy._element_constructor_attributes` + + INPUT: + + - ``constraints`` -- a bunch of constraints + + TESTS:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: pol = TopMostParentPolicy(XYPairs, (), XYPair) + sage: pol._element_constructor_attributes(()) + {'_parent_for': 'self', 'Element': } + sage: pol._element_constructor_attributes((1)) + {'element_class': , '_facade_for': AllPairs, '_parent_for': AllPairs} + """ + factory = self._factory + if constraints == self._top_constraints: + return self._self_element_constructor_attributes(self._Element) + else: + return self._facade_element_constructor_attributes( + factory(*self._top_constraints, policy=self)) + + def _repr_(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import * + sage: TopMostParentPolicy(XYPairs, (), XYPair) # indirect doctest + Set factory policy for with parent AllPairs[=Factory for XY pairs(())] + """ + return "Set factory policy for %s with parent %s[=%s(%s)]"%( + self._Element, self._factory(self._top_constraints), + self._factory, self._top_constraints) + + +class FacadeParentPolicy(SetFactoryPolicy): + r""" + Policy for facade parent + + INPUT: + + - ``factory`` -- an instance of :class:`SetFactory` + - ``parent`` -- an instance of :class:`Parent` + + Given a factory ``F`` an a class ``E``, returns a policy for parent ``P`` + creating element as if they were created by ``parent`` + + EXAMPLES:: + + sage: from sage.structure.set_factories import * + sage: from sage.structure.set_factories_example import * + + We create a custom standard parent ``P``:: + + sage: selfpolicy = SelfParentPolicy(XYPairs, XYPair) + sage: P = XYPairs(x=2, policy=selfpolicy) + sage: pol = FacadeParentPolicy(XYPairs, P) + sage: P2 = XYPairs(x=2, y=3, policy=pol) + sage: el = P2.an_element() + sage: el.parent() is P + True + sage: type(el) is P.element_class + True + + If ``parent`` is itself a facade parent, then transitivity is correctly + applied:: + + sage: P = XYPairs() + sage: P2 = XYPairs(x=2) + sage: P2.category() + Category of facade finite enumerated sets + sage: pol = FacadeParentPolicy(XYPairs, P) + sage: P23 = XYPairs(x=2, y=3, policy=pol) + sage: el = P2.an_element() + sage: el.parent() is P + True + sage: type(el) is P.element_class + True + """ + def __init__(self, factory, parent): + r""" + TEST:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: F = FacadeParentPolicy(XYPairs, XYPairs()); F + Set factory policy for facade parent AllPairs + sage: TestSuite(F).run(skip='_test_category') + """ + self._parent_for = parent + SetFactoryPolicy.__init__(self, factory) + + def category(self, constraints): + r""" + Return the policy category for given constraints + + Return the policy category associated to ``self`` for parent + constructed with the given constraints as per + :meth:`SetFactoryPolicy.category`. Here constraints are ignored. + + INPUT: + + - ``constraints`` -- a set of constraints (ignored) + + OUTPUT: + + - an instance of :class:`FacadeParentPolicyCategory`. + + EXAMPLE:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: F = FacadeParentPolicy(XYPairs, XYPairs()) + """ + # assert(isinstance(constraints, tuple)) + return FacadeParentPolicyCategory(self._parent_for) + + def _element_constructor_attributes(self, constraints): + r""" + Returns the element constructor attributes as per + :meth:`SetFactoryPolicy._element_constructor_attributes` + + INPUT: + + - ``constraints`` -- a bunch of constraints + + TESTS:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: pol = FacadeParentPolicy(XYPairs, XYPairs()) + sage: pol._element_constructor_attributes(()) + {'element_class': , '_facade_for': AllPairs, '_parent_for': AllPairs} + sage: pol._element_constructor_attributes((1)) + {'element_class': , '_facade_for': AllPairs, '_parent_for': AllPairs} + """ + return self._facade_element_constructor_attributes( + self._parent_for._parent_for) + + def _repr_(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories import FacadeParentPolicy + sage: from sage.structure.set_factories_example import * + sage: FacadeParentPolicy(XYPairs, XYPairs()) # indirect doctest + Set factory policy for facade parent AllPairs + """ + return "Set factory policy for facade parent %s"%( + self._parent_for) + + +#################################################### +# Parent # +#################################################### + +class SetFactoryParent(Parent): + r""" + Abstract class for parent belonging to a set factory + + INPUT: + + - ``constraints`` -- a set of constraints + - ``policy`` -- the policy for element construction + - ``category`` -- the category of the parent (default to ``None``) + + Depending on the constraints and the policy, initialize the parent in a + proper category to set up element construction. + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import * + sage: from sage.structure.set_factories import * + sage: P = PairsX_(3, XYPairs._default_policy) + sage: P is XYPairs(3) + True + sage: P.category() + Category of facade finite enumerated sets + """ + def __init__(self, constraints, policy, category = None): + r""" + TESTS:: + + sage: from sage.structure.set_factories import SetFactoryParent + sage: from sage.structure.set_factories_example import XYPairs + sage: isinstance(XYPairs(3), SetFactoryParent) # indirect doctest + True + """ + # assert(isinstance(constraints, tuple)) + self._constraints = constraints + assert(isinstance(policy, SetFactoryPolicy)) + self._policy = policy + policy_attributes = policy._element_constructor_attributes(constraints) + # print self._constraints, policy_attributes + for attrname, attr in policy_attributes.items(): + if attr == "self": + setattr(self, attrname, self) + else: + setattr(self, attrname, attr) + assert(isinstance(self._parent_for, Parent)) + if '_facade_for' in attrname: + category = Sets().Facades().or_subcategory(category) + Parent.__init__(self, + category = Sets().or_subcategory(category), + facade = policy_attributes.get('_facade_for', None)) + + + def constraints(self): + r""" + Return the constraints for ``self`` + + Currently there is no specification on how constraints are handled. + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs().constraints() + () + sage: XYPairs(x=3).constraints() + (3,) + sage: XYPairs(y=2).constraints() + (None, 2) + """ + return self._constraints + + def policy(self): + r""" + Return the policy used when ``self`` was created + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs().policy() + Set factory policy for with parent AllPairs[=Factory for XY pairs(())] + sage: XYPairs(x=3).policy() + Set factory policy for with parent AllPairs[=Factory for XY pairs(())] + """ + return self._policy + + def facade_policy(self): + r""" + Return the policy for parent facade for ``self`` + + EXAMPLES:: + + sage: from sage.structure.set_factories import * + sage: from sage.structure.set_factories_example import * + + We create a custom standard parent ``P``:: + + sage: selfpolicy = SelfParentPolicy(XYPairs, XYPair) + sage: P = XYPairs(x=2, policy=selfpolicy) + sage: P.facade_policy() + Set factory policy for facade parent {(2, b) | b in range(5)} + + Now passing ``P.facade_policy()`` creates parent which are facade for + ``P``:: + + sage: P3 = XYPairs(x=2, y=3, policy=P.facade_policy()) + sage: P3.facade_for() == (P,) + True + sage: el = P3.an_element() + sage: el.parent() is P + True + """ + return FacadeParentPolicy(self.factory(), self) + + def factory(self): + r""" + Return the factory which built ``self`` + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs().factory() is XYPairs + True + sage: XYPairs(x=3).factory() is XYPairs + True + """ + return self._policy.factory() + + def subset(self, *args, **options): + r""" + Return a subset of ``self`` by adding more constraints + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: S = XYPairs() + sage: S3 = S.subset(x=3) + sage: S3.list() + [(3, 0), (3, 1), (3, 2), (3, 3), (3, 4)] + + TESTS:: + + sage: S3 is XYPairs(3) + True + sage: S3 is XYPairs(x=3) + True + """ + factory = self.factory() + constr = factory.add_constraints(self._constraints, + (args, options)) + return factory(*constr, policy = self._policy, **options) + + def _test_subset(self, **options): + r""" + Tests that subsets with no extra parameters returns ``self`` + + Currently only test that one gets the same parent is no more + constraints are added. + + .. TODO:: + + Straighten the test when handling of constraints will be + specified. + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: S = XYPairs() + sage: S._test_subset() + """ + #TODO verifie avec self.constraints + tester = self._tester(**options) + tester.assertTrue(self.subset() is self) + + @abstract_method + def check_element(self, x, check): + r""" + Check that ``x`` verifies the constraints of ``self`` + + INPUT: + + - ``x`` -- an instance of ``self.element_class``. + + - ``check`` -- the level of checking to be performed (usually a + boolean). + + This method may assume that ``x`` was properly constructed by ``self`` + or a possible super-set of ``self`` for which ``self`` is a facade. It + should return nothing is ``x`` verifies the constraints and raise a + :exc:`~exceptions.ValueError` explaining which constraints ``x`` + fails otherwise. + + The method should accept an extra parameter check specifying which + level of check should be performed. It will only be called when + ``bool(check)`` evaluates to ``True``. + + .. TODO:: Should we always call check element and let it decide which + check has to be performed ? + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: S = XYPairs() + sage: el = S((2,3)) + sage: S.check_element(el, True) + sage: XYPairs(x=2).check_element(el, True) + sage: XYPairs(x=3).check_element(el, True) + Traceback (most recent call last): + ... + ValueError: Wrong first coordinate + sage: XYPairs(y=4).check_element(el, True) + Traceback (most recent call last): + ... + ValueError: Wrong second coordinate + """ + + def __contains__(self, x): + r""" + Default implementation for ``__contains__``: + + INPUT:: + + - ``x`` -- any object + + Check for class, parent and calls ``self.check_element(x)`` + + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: S = XYPairs() + sage: el = S((2,3)) + sage: el in S + True + sage: el in XYPairs(x=2) + True + sage: el in XYPairs(x=3) + False + sage: el in XYPairs(y=4) + False + """ + if (isinstance(x, self.element_class) and + x.parent() == self._parent_for): # TODO: is_parent_of ??? + try: + self.check_element(x, True) + except ValueError: + return False + else: + return True + else: + return False + + def __call__(self, *args, **keywords): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: S = XYPairs() + sage: el = S((2,3)); el + (2, 3) + sage: S(el) is el + True + + sage: XYPairs(x=3)((2,3)) + Traceback (most recent call last): + ... + ValueError: Wrong first coordinate + + sage: XYPairs(x=3)(el) + Traceback (most recent call last): + ... + ValueError: Wrong first coordinate + """ + # Ensure idempotence of element construction + if (len(args) == 1 and + isinstance(args[0], self.element_class) and + args[0].parent() == self._parent_for): + check = keywords.get("check", True) + if check: + self.check_element(args[0], check) + return args[0] + else: + return Parent.__call__(self, *args, **keywords) + + # QUESTION: Should we call: + # self._parent_for._element_constructor_ + # currently we don't call it directly because: + # - it may do some extra check we dont want to perform ? + # - calling directly element_class should be faster + def _element_constructor_(self, *args, **keywords): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs()((2,3)) # indirect doctest + (2, 3) + sage: XYPairs(x=3)((3,3)) # indirect doctest + (3, 3) + sage: XYPairs(x=3)((2,3)) # indirect doctest + Traceback (most recent call last): + ... + ValueError: Wrong first coordinate + + sage: XYPairs(x=3)((2,3), check=False) # Don't do this at home, kids + (2, 3) + """ + check = keywords.get("check", True) + res = self.element_class(self._parent_for, *args, **keywords) + if check: + self.check_element(res, check) + return res + diff --git a/src/sage/structure/set_factories_example.py b/src/sage/structure/set_factories_example.py new file mode 100644 index 00000000000..9c1a5a92112 --- /dev/null +++ b/src/sage/structure/set_factories_example.py @@ -0,0 +1,407 @@ +r""" +An example of set factory +========================= + +The goal of this module is to exemplify the use of set factories. Note that +the written code is intentionally kept minimal; many things and in particular +several iterators could be written in a more efficient way. + +.. SEEALSO:: :mod:`.set_factories` for an introduction to set + factories, their specifications, and examples of their use and + implementation based on this module. + +""" +#***************************************************************************** +# Copyright (C) 2012 Florent Hivert +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element_wrapper import ElementWrapper +from sage.structure.set_factories import ( + SetFactory, SetFactoryParent, TopMostParentPolicy) +from sage.sets.all import DisjointUnionEnumeratedSets +from sage.sets.family import LazyFamily +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.rings.integer import Integer +from sage.misc.lazy_attribute import lazy_attribute + +MAX = 5 + +class XYPairsFactory(SetFactory): + r""" + An example of factory for sets of pairs of integers + + .. SEEALSO:: :mod:`.set_factories` for an introduction to factories. + """ + def __call__(self, x=None, y=None, policy=None): + r""" + Construct the subset from constraints + + Consider the set `S` of couple `(x,y)` with `x` and `y` in + `I:=\{0,1,2,3,4\}`. Returns the subsets of element of `S` satisfying + some constraints. + + INPUT: + + - ``x=a`` -- where ``a`` is an integer (default to ``None``). + - ``y=b`` -- where ``b`` is an integer (default to ``None``). + - ``policy`` -- the policy passed to the created set. + + .. SEEALSO:: :class:`.set_factories.SetFactoryPolicy` + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import XYPairsFactory + sage: XYPairs = XYPairsFactory() + sage: P = XYPairs(); P.list() + [(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)] + + .. note:: This function is actually the ``__call__`` method of + :class:`XYPairsFactory`. + """ + if policy is None: + policy = self._default_policy + + if isinstance(x, (Integer, int)): + if isinstance(y, (Integer, int)): + return SingletonPair(x, y, policy) + else: + return PairsX_(x, policy) + elif isinstance(y, (Integer, int)): + return Pairs_Y(y, policy) + else: + return AllPairs(policy) + + def add_constraints(self, cons, (args, opts)): + r""" + Add constraints to the set ``cons`` as per :meth:`SetFactory.add_constraints <.set_factories.SetFactory.add_constraints>` + + This is a very crude implementation which ignore optional arguments. + They will be handled directly by the factory :func:`XYPair` + + EXAMPLE:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs.add_constraints((3,), ((2,), {})) + (3, 2) + sage: XYPairs.add_constraints((), ((2,), {})) + (2,) + """ + return cons+args + + @lazy_attribute + def _default_policy(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairsFactory + sage: XYPairs = XYPairsFactory() + sage: XYPairs._default_policy + Set factory policy for with parent AllPairs[=Factory for XY pairs(())] + """ + return TopMostParentPolicy(self, (), XYPair) + + def _repr_(self): + """ + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs # indirect doctest + Factory for XY pairs + """ + return "Factory for XY pairs" + +XYPairs = XYPairsFactory() +XYPairs.__doc__ = XYPairsFactory.__call__.__doc__ + +class XYPair(ElementWrapper): + r""" + A class for Element `(x,y)` with `x` and `y` in `\{0,1,2,3,4\}` + + EXAMPLES:: + + sage: from sage.structure.set_factories_example import * + sage: p = XYPair(Parent(), (0,1)); p + (0, 1) + sage: p = XYPair(Parent(), (0,8)) + Traceback (most recent call last): + ... + ValueError: numbers must be in range(5) + """ + def __init__(self, parent, value, check=True): + """ + TESTS:: + + sage: from sage.structure.set_factories_example import * + sage: P = XYPairs(); p = P.list()[0] + sage: TestSuite(p).run() + """ + if check: + if not isinstance(value, tuple): + raise ValueError, "Value %s must be a tuple"%value + if len(value) != 2: + raise ValueError, "Value must be of length 2" + if not all(int(x) in range(MAX) for x in value): + raise ValueError, "numbers must be in range(%s)"%MAX + ElementWrapper.__init__(self, value, parent) + +class AllPairs(SetFactoryParent, DisjointUnionEnumeratedSets): + r""" + This parent show how one can use set factories together with + :class:`DisjointUnionEnumeratedSets` + + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: P = XYPairs(); P.list() + [(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)] + """ + def __init__(self, policy): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: TestSuite(XYPairs()).run() + """ + SetFactoryParent.__init__(self, (), policy, + category = FiniteEnumeratedSets()) + DisjointUnionEnumeratedSets.__init__( + self, LazyFamily(range(MAX), self._single_pair), + facade=True, keepkey = False, + category = self.category()) + + def _single_pair(self, letter): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs()._single_pair(1) + {(a, 1) | a in range(5)} + """ + return Pairs_Y(letter, policy=self.facade_policy()) + + def _repr_(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs() # indirect doctest + AllPairs + """ + return "AllPairs" + + def check_element(self, el, check): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: P = XYPairs() + sage: P.check_element(P.an_element(), True) + sage: XYPairs()((7, 0)) # indirect doctest + Traceback (most recent call last): + ... + ValueError: numbers must be in range(5) + """ + pass + +class PairsX_(SetFactoryParent, UniqueRepresentation): + r""" + The set of pair `(x, 0), (x, 1), ..., (x, 4)` + + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: P = XYPairs(0); P.list() + [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)] + """ + def __init__(self, x, policy): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: TestSuite(XYPairs(0)).run() + """ + self._x = x + SetFactoryParent.__init__(self, (x,), policy, + category = FiniteEnumeratedSets()) + + def _repr_(self): + """ + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs(x=1) + {(1, b) | b in range(5)} + """ + return "{(%s, b) | b in range(%s)}"%(self._x, MAX) + + def an_element(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: P = XYPairs(x=0); P.an_element() + (0, 0) + """ + return self._element_constructor_((self._x, 0), check=False) + + def check_element(self, el, check): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: P = XYPairs(x=1) + sage: P.check_element(P.an_element(), True) + sage: XYPairs(x=1)((0, 0)) # indirect doctest + Traceback (most recent call last): + ... + ValueError: Wrong first coordinate + """ + (x, y) = el.value + if x != self._x: + raise ValueError, "Wrong first coordinate" + + def __iter__(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: list(XYPairs(x=1)) + [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4)] + """ + for i in range(MAX): + yield self._element_constructor_((self._x, i), check=False) + + + +class Pairs_Y(SetFactoryParent, DisjointUnionEnumeratedSets): + r""" + The set of pair `(0, y), (1, y), ..., (4, y)` + + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: P = XYPairs(y=1); P.list() + [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)] + """ + def __init__(self, y, policy): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: TestSuite(XYPairs(y=1)).run() + """ + self._y = y + SetFactoryParent.__init__(self, (None, y), policy, + category = FiniteEnumeratedSets()) + DisjointUnionEnumeratedSets.__init__( + self, LazyFamily(range(MAX), self._single_pair), + facade=True, keepkey = False, + category = self.category()) # TODO remove and fix disjoint union. + + def _repr_(self): + """ + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs(y=1) + {(a, 1) | a in range(5)} + """ + return "{(a, %s) | a in range(%s)}"%(self._y, MAX) + + def an_element(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs(y=1).an_element() + (0, 1) + """ + return self._element_constructor_((0, self._y), check=False) + + def _single_pair(self, letter): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs(y=1)._single_pair(0) + {(0, 1)} + """ + return SingletonPair(letter, self._y, policy=self.facade_policy()) + + def check_element(self, el, check): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: P = XYPairs(y=1) + sage: P.check_element(P.an_element(), True) + sage: XYPairs(y=1)((1, 0)) # indirect doctest + Traceback (most recent call last): + ... + ValueError: Wrong second coordinate + """ + (x, y) = el.value + if y != self._y: + raise ValueError, "Wrong second coordinate" + + +class SingletonPair(SetFactoryParent, UniqueRepresentation): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: P = XYPairs(0,1); P.list() + [(0, 1)] + """ + def __init__(self, x, y, policy): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: TestSuite(XYPairs(0,1)).run() + """ + self._xy = (x, y) + SetFactoryParent.__init__(self, (x, y), policy, + category = FiniteEnumeratedSets()) + + def _repr_(self): + """ + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs(x=2, y=1) + {(2, 1)} + """ + return "{%s}"%(self._xy,) + + def check_element(self, el, check): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: XYPairs(0,1).check_element(XYPairs()((0,1)), True) + sage: XYPairs(0,1).check_element(XYPairs()((1,0)), True) + Traceback (most recent call last): + ... + ValueError: Wrong coordinate + sage: XYPairs(0,1)((1,1)) + Traceback (most recent call last): + ... + ValueError: Wrong coordinate + """ + if el.value != self._xy: + raise ValueError, "Wrong coordinate" + + def __iter__(self): + r""" + TESTS:: + + sage: from sage.structure.set_factories_example import XYPairs + sage: list(XYPairs(0,1)) + [(0, 1)] + """ + yield self._element_constructor_(self._xy, check=False) From 272d17fe941631f36d1107df6901cfb95b300e38 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Tue, 8 Apr 2014 11:02:27 +0200 Subject: [PATCH 02/11] Fixed passing parent to ElementWrapper --- src/sage/structure/set_factories_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/structure/set_factories_example.py b/src/sage/structure/set_factories_example.py index 9c1a5a92112..547e8a9c77a 100644 --- a/src/sage/structure/set_factories_example.py +++ b/src/sage/structure/set_factories_example.py @@ -146,7 +146,7 @@ def __init__(self, parent, value, check=True): raise ValueError, "Value must be of length 2" if not all(int(x) in range(MAX) for x in value): raise ValueError, "numbers must be in range(%s)"%MAX - ElementWrapper.__init__(self, value, parent) + ElementWrapper.__init__(self, parent, value) class AllPairs(SetFactoryParent, DisjointUnionEnumeratedSets): r""" From 1491bff5a56efadefb89d016e5f5d2082e952229 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Tue, 8 Apr 2014 17:25:38 +0200 Subject: [PATCH 03/11] Cleanup in constraints managment. --- src/sage/structure/set_factories.py | 32 +++++++-------- src/sage/structure/set_factories_example.py | 45 ++++++++++++++------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index 64fd8b0ccd4..31365779cf4 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -76,8 +76,8 @@ Set factories provide an alternative way to build subsets of an already constructed set: each set constructed by a factory has a method -:meth:`~SetFactoryParent.subset` which accept new constraints. Set -constructed by the factory or the :meth:`~SetFactoryParent.subset` methods are +:meth:`~ParentWithSetFactory.subset` which accept new constraints. Set +constructed by the factory or the :meth:`~ParentWithSetFactory.subset` methods are identical:: sage: Sx2s = S.subset(x=2); Sx2 is Sx2s @@ -90,7 +90,7 @@ sage: S23.subset(y=5) Traceback (most recent call last): ... - TypeError: __call__() got multiple values for keyword argument 'y' + ValueError: Duplicate value for constraints 'y': was 3 now 5 .. RUBRIC:: Constructing custom elements: policies @@ -207,8 +207,8 @@ .. RUBRIC:: Technicalities: how policies inform parents Parent built from factories should inherits from -:class:`SetFactoryParent`. This class provide a few methods related to -factories and policies. The ``__init__`` method of :class:`SetFactoryParent` +:class:`ParentWithSetFactory`. This class provide a few methods related to +factories and policies. The ``__init__`` method of :class:`ParentWithSetFactory` must be provided with the set of constraints and the policy. A parent built from a factory must creates element trough a call to the method ``_element_constructor_``. The current way policies inform parents how to @@ -223,13 +223,13 @@ A parent built from a factory should -- *inherit* from :class:`SetFactoryParent`. It should accept a ``policy`` +- *inherit* from :class:`ParentWithSetFactory`. It should accept a ``policy`` argument and pass it verbatim to the ``__init__`` method of - :class:`SetFactoryParent` together with the set of constraints; + :class:`ParentWithSetFactory` together with the set of constraints; - *create its elements* trough calls to the method ``_element_constructor_``; -- *define a method* :class:`SetFactoryParent.check_element` which checks if a +- *define a method* :class:`ParentWithSetFactory.check_element` which checks if a built element indeed belongs to it. The method should accept an extra keyword parameter called ``check`` specifying which level of check should be performed. It will only be called when ``bool(check)`` evaluates to @@ -325,7 +325,7 @@ def add_constraints(self, cons, *args, **opts): TESTS:: sage: from sage.structure.set_factories_example import XYPairs - sage: XYPairs.add_constraints((3,),((2,), {})) + sage: XYPairs.add_constraints((3,),((None, 2), {})) (3, 2) """ @@ -339,7 +339,7 @@ class SetFactoryPolicy(UniqueRepresentation, SageObject): Abstract base class for policies. A policy is a device which is passed to a parent inheriting from - :class:`SetFactoryParent` in order to set-up the element construction + :class:`ParentWithSetFactory` in order to set-up the element construction framework. INPUT: @@ -438,7 +438,7 @@ def _element_constructor_attributes(self, constraints): Should returns the attributes that are prerequisite for element construction. This is coordinated with - :meth:`SetFactoryParent._element_constructor_`. Currently to standard + :meth:`ParentWithSetFactory._element_constructor_`. Currently to standard attributes are provided in :meth:`_facade_element_constructor_attributes` and :meth:`_self_element_constructor_attributes`. You should returns to @@ -720,7 +720,7 @@ def _repr_(self): # Parent # #################################################### -class SetFactoryParent(Parent): +class ParentWithSetFactory(Parent): r""" Abstract class for parent belonging to a set factory @@ -747,9 +747,9 @@ def __init__(self, constraints, policy, category = None): r""" TESTS:: - sage: from sage.structure.set_factories import SetFactoryParent + sage: from sage.structure.set_factories import ParentWithSetFactory sage: from sage.structure.set_factories_example import XYPairs - sage: isinstance(XYPairs(3), SetFactoryParent) # indirect doctest + sage: isinstance(XYPairs(3), ParentWithSetFactory) # indirect doctest True """ # assert(isinstance(constraints, tuple)) @@ -783,7 +783,7 @@ def constraints(self): sage: XYPairs().constraints() () sage: XYPairs(x=3).constraints() - (3,) + (3, None) sage: XYPairs(y=2).constraints() (None, 2) """ @@ -867,7 +867,7 @@ def subset(self, *args, **options): factory = self.factory() constr = factory.add_constraints(self._constraints, (args, options)) - return factory(*constr, policy = self._policy, **options) + return factory(*constr, policy = self._policy) def _test_subset(self, **options): r""" diff --git a/src/sage/structure/set_factories_example.py b/src/sage/structure/set_factories_example.py index 547e8a9c77a..79dd3a271b6 100644 --- a/src/sage/structure/set_factories_example.py +++ b/src/sage/structure/set_factories_example.py @@ -21,7 +21,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element_wrapper import ElementWrapper from sage.structure.set_factories import ( - SetFactory, SetFactoryParent, TopMostParentPolicy) + SetFactory, ParentWithSetFactory, TopMostParentPolicy) from sage.sets.all import DisjointUnionEnumeratedSets from sage.sets.family import LazyFamily from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -82,15 +82,32 @@ def add_constraints(self, cons, (args, opts)): This is a very crude implementation which ignore optional arguments. They will be handled directly by the factory :func:`XYPair` - EXAMPLE:: + EXAMPLES:: sage: from sage.structure.set_factories_example import XYPairs - sage: XYPairs.add_constraints((3,), ((2,), {})) - (3, 2) + sage: XYPairs.add_constraints((3,None), ((2,), {})) + Traceback (most recent call last): + ... + ValueError: Duplicate value for constraints 'x': was 3 now 2 sage: XYPairs.add_constraints((), ((2,), {})) - (2,) + (2, None) + sage: XYPairs.add_constraints((), ((2,), {'y':3})) + (2, 3) """ - return cons+args + res = list(cons) + res += [None]*(2-len(res)) + def set_args(argss): + for i, v in enumerate(argss): + if res[i] is not None and v is not None: + raise ValueError, "Duplicate value for constraints '%s': was %s now %s"%( + ['x','y'][i], res[i], v) + if v is not None: res[i] = v + set_args(args) + def parse_args(x=None,y=None): set_args((x,y)) + parse_args(**opts) + if res == (None, None): + return () + return tuple(res) @lazy_attribute def _default_policy(self): @@ -148,7 +165,7 @@ def __init__(self, parent, value, check=True): raise ValueError, "numbers must be in range(%s)"%MAX ElementWrapper.__init__(self, parent, value) -class AllPairs(SetFactoryParent, DisjointUnionEnumeratedSets): +class AllPairs(ParentWithSetFactory, DisjointUnionEnumeratedSets): r""" This parent show how one can use set factories together with :class:`DisjointUnionEnumeratedSets` @@ -166,7 +183,7 @@ def __init__(self, policy): sage: from sage.structure.set_factories_example import XYPairs sage: TestSuite(XYPairs()).run() """ - SetFactoryParent.__init__(self, (), policy, + ParentWithSetFactory.__init__(self, (), policy, category = FiniteEnumeratedSets()) DisjointUnionEnumeratedSets.__init__( self, LazyFamily(range(MAX), self._single_pair), @@ -207,7 +224,7 @@ def check_element(self, el, check): """ pass -class PairsX_(SetFactoryParent, UniqueRepresentation): +class PairsX_(ParentWithSetFactory, UniqueRepresentation): r""" The set of pair `(x, 0), (x, 1), ..., (x, 4)` @@ -225,7 +242,7 @@ def __init__(self, x, policy): sage: TestSuite(XYPairs(0)).run() """ self._x = x - SetFactoryParent.__init__(self, (x,), policy, + ParentWithSetFactory.__init__(self, (x,None), policy, category = FiniteEnumeratedSets()) def _repr_(self): @@ -277,7 +294,7 @@ def __iter__(self): -class Pairs_Y(SetFactoryParent, DisjointUnionEnumeratedSets): +class Pairs_Y(ParentWithSetFactory, DisjointUnionEnumeratedSets): r""" The set of pair `(0, y), (1, y), ..., (4, y)` @@ -295,7 +312,7 @@ def __init__(self, y, policy): sage: TestSuite(XYPairs(y=1)).run() """ self._y = y - SetFactoryParent.__init__(self, (None, y), policy, + ParentWithSetFactory.__init__(self, (None, y), policy, category = FiniteEnumeratedSets()) DisjointUnionEnumeratedSets.__init__( self, LazyFamily(range(MAX), self._single_pair), @@ -349,7 +366,7 @@ def check_element(self, el, check): raise ValueError, "Wrong second coordinate" -class SingletonPair(SetFactoryParent, UniqueRepresentation): +class SingletonPair(ParentWithSetFactory, UniqueRepresentation): r""" TESTS:: @@ -365,7 +382,7 @@ def __init__(self, x, y, policy): sage: TestSuite(XYPairs(0,1)).run() """ self._xy = (x, y) - SetFactoryParent.__init__(self, (x, y), policy, + ParentWithSetFactory.__init__(self, (x, y), policy, category = FiniteEnumeratedSets()) def _repr_(self): From 41c137e5a6ca28c27f1c21df3670578da370cacb Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Tue, 8 Apr 2014 18:54:47 +0200 Subject: [PATCH 04/11] Fixed printing of _repr_ for TopMostParentPolicy --- src/sage/structure/set_factories.py | 34 ++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index 31365779cf4..72bbd32bb8c 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -252,8 +252,9 @@ - *define a method* overloading :meth:`SetFactory.add_constraints` which is responsible of computing the union of two sets of constraints; -- *optionally* define an attribute ``_default_policy`` used by call to pass to - the parent. +- *optionally* define a method or an attribute ``_default_policy`` passed to + the :class:`ParentWithSetFactory` if no policy is given to the factory. + .. TODO:: There is currently no support for dealing with sets of constraints. The set factory and the parents must cooperate to @@ -312,6 +313,17 @@ def __call__(self, *constraints, **consdict): sage: XYPairs(x=3) {(3, b) | b in range(5)} + sage: XYPairs(y=2) + {(a, 2) | a in range(5)} + + TESTS:: + + sage: from sage.structure.set_factories import SetFactory + sage: F = SetFactory() + sage: F() + Traceback (most recent call last): + ... + NotImplementedError: """ @abstract_method @@ -322,11 +334,23 @@ def add_constraints(self, cons, *args, **opts): Should return a set of constraints. Currently there is no specification on how constraints are passed as arguments. - TESTS:: + EXAMPLES:: sage: from sage.structure.set_factories_example import XYPairs sage: XYPairs.add_constraints((3,),((None, 2), {})) (3, 2) + + sage: XYPairs.add_constraints((3,),((None, None), {'y': 2})) + (3, 2) + + TESTS:: + + sage: from sage.structure.set_factories import SetFactory + sage: F = SetFactory() + sage: F.add_constraints(()) + Traceback (most recent call last): + ... + NotImplementedError: """ # TODO ? default policy @@ -537,7 +561,7 @@ class TopMostParentPolicy(SetFactoryPolicy): - ``Element`` -- a subclass of :class:`~.element.Element` Given a factory ``F`` and a class ``E``, returns a policy for parent ``P`` - creating element in class ``E`` and parent ``factory(top_constraints)``. + creating element in class ``E`` and parent ``factory(*top_constraints, policy)``. EXAMPLES:: @@ -595,7 +619,7 @@ def _repr_(self): Set factory policy for with parent AllPairs[=Factory for XY pairs(())] """ return "Set factory policy for %s with parent %s[=%s(%s)]"%( - self._Element, self._factory(self._top_constraints), + self._Element, self._factory(*self._top_constraints, policy=self), self._factory, self._top_constraints) From f76e0e60f636bcd86b0343c1178739db03752e9b Mon Sep 17 00:00:00 2001 From: Nicolas Borie Date: Fri, 11 Apr 2014 14:02:17 +0200 Subject: [PATCH 05/11] Review : fonctionality, documentations, tests (very small things remains...) --- src/sage/structure/set_factories.py | 238 ++++++++++---------- src/sage/structure/set_factories_example.py | 31 ++- 2 files changed, 144 insertions(+), 125 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index 72bbd32bb8c..b3cb0803bf2 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -3,33 +3,33 @@ ============= -A *set factory* `F` is a device for constructing :class:`Parent`s `P` -that model subsets of a big set `S`. Typically, each such parent is -constructed as the subset of `S` of all elements satisfying a certain -collection of constraints `cons`. In such a hierarchy of subsets, one -needs easy and flexible control on how elements are construted. For -example, one may want to construct the elements of `P` in some -subclass of the class of the elements of `S`. On other occasions, one -also often needs `P` to be a facade parent, whose elements are -represented as elements of `S` (see -:func:`~sage.categories.facade_sets.Facade`). +A *set factory* `F` is a device for constructing some :class:`Parent` +`P` that model subsets of a big set `S`. Typically, each such parent +is constructed as the subset of `S` of all elements satisfying a +certain collection of constraints `cons`. In such a hierarchy of +subsets, one needs an easy and flexible control on how elements are +constructed. For example, one may want to construct the elements of +`P` in some subclass of the class of the elements of `S`. On other +occasions, one also often needs `P` to be a facade parent, whose +elements are represented as elements of `S` (see +:class:`~sage.categories.facade_sets.FacadeSets`). The role of a set factory is twofold: -- *manage a database* of constructors for the different parents `P = F(cons)` +- *Manage a database* of constructors for the different parents `P = F(cons)` depending on the various kinds of constraints `cons`. Note: currently there is no real support for that. We are gathering use cases before fixing the interface. -- ensure that the elements `e = P(...)` created by the different parents +- Ensure that the elements `e = P(...)` created by the different parents follows a consistent policy concerning their *class and parent*. .. RUBRIC:: Basic usage: constructing parents through a factory The file :mod:`sage.structure.set_factories_example` shows an example of a :class:`SetFactory` together with typical implementation. Note that the -written code is intentionnally kept minimal, many thing and in particular -several iterator could be written in a more efficient ways. +written code is intentionally kept minimal, many things and in particular +several iterators could be written in a more efficient way. Consider the set `S` of couple `(x,y)` with `x` and `y` in `I:=\{0,1,2,3,4\}`. We represent the element of `S` as 2-elements tuple, wrapped in a class @@ -52,7 +52,7 @@ S_a^b := \{(x,y) \in S \mid x = a, y = b\}. The constraints considered here are admittedly trivial. In a realistic -examples, there would be much more of them. And for some sets of constraints +example, there would be much more of them. And for some sets of constraints no good enumeration algorithms would be known. In Sage, those sets are constructed passing the constraints to the factory. We @@ -76,7 +76,7 @@ Set factories provide an alternative way to build subsets of an already constructed set: each set constructed by a factory has a method -:meth:`~ParentWithSetFactory.subset` which accept new constraints. Set +:meth:`~ParentWithSetFactory.subset` which accept new constraints. Sets constructed by the factory or the :meth:`~ParentWithSetFactory.subset` methods are identical:: @@ -85,7 +85,7 @@ sage: Sx2.subset(y=3) is S23 True -It is not possible to change an already given constraints:: +It is not possible to change an already given constraint:: sage: S23.subset(y=5) Traceback (most recent call last): @@ -122,7 +122,7 @@ is a lot of flexibility to change that. We now illustrate how to make two different choices. -1 - In a first use case, we want to add some method to the constructed +1 - In a first use case, we want to add some methods to the constructed elements. As illustration, we add here a new method ``sum`` which returns `x+y`. We therefore inherit from :class:`~.set_factories_example.XYPair`:: @@ -134,12 +134,12 @@ 5 We now want to have subsets generating those new elements while still having a -single real parent (the one with no constraints) for each element. The +single real parent (the one with no constraint) for each element. The corresponding policy is called :class:`TopMostParentPolicy`. It takes tree parameters: - the factory; -- the parameters for void constraints; +- the parameters for void constraint; - the class used for elements. Calling the factory with this policy returns a new set which builds its @@ -154,7 +154,7 @@ sage: el.parent() is newS True -Subsets now inherits the policy:: +Subsets now inherit the policy:: sage: newS2 = newS.subset(x=2) sage: el2 = newS2.an_element(); el2 @@ -164,8 +164,8 @@ sage: el2.parent() is newS True -2 - In a second use case, we want the elements to remember which parent -created them. The corresponding policy is called +2 - In a second use case, we want the elements to remember which +parent created them. The corresponding policy is called :class:`SelfParentPolicy`. It takes only two parameters: - the factory; @@ -179,7 +179,7 @@ sage: el.parent() is selfS True -Now subsets are the parent of the element they created:: +Now subsets are parent of the element they create:: sage: selfS2 = selfS.subset(x=2) sage: el2 = selfS2.an_element() @@ -193,10 +193,11 @@ - :class:`FacadeParentPolicy`: reuse an existing parent together with its element_class -- :class:`TopMostParentPolicy`: use a parent created by the factory itself and - provide a class ``Element`` for it. In this case, we need to specify for - which sets of constraints the constructed parent needs to be provided with - a class ``Element``. +- :class:`TopMostParentPolicy`: use a parent created by the factory + itself and provide a class ``Element`` for it. In this case, we need + to specify the set of constraints which build this parent taking the + ownership of all elements and the class which will be use for the + ``Element``. - :class:`SelfParentPolicy`: provide systematically Element and element_class and ensure that the parent is ``self``. @@ -207,13 +208,15 @@ .. RUBRIC:: Technicalities: how policies inform parents Parent built from factories should inherits from -:class:`ParentWithSetFactory`. This class provide a few methods related to -factories and policies. The ``__init__`` method of :class:`ParentWithSetFactory` -must be provided with the set of constraints and the policy. A parent built -from a factory must creates element trough a call to the method -``_element_constructor_``. The current way policies inform parents how to -builds their elements is by setting a few attributes. So the class must accept -attribute adding. The precise details of which attribute are set may be subject to change in the future. +:class:`ParentWithSetFactory`. This class provide a few methods +related to factories and policies. The ``__init__`` method of +:class:`ParentWithSetFactory` must be provided with the set of +constraints and the policy. A parent built from a factory must creates +elements trough a call to the method ``_element_constructor_``. The +current way policies inform parents how to builds their elements is +setted by a few attributes. So the class must accept attribute +adding. The precise details of which attribute are set may be subject +to change in the future. .. RUBRIC:: How to write a set factory @@ -223,17 +226,19 @@ A parent built from a factory should -- *inherit* from :class:`ParentWithSetFactory`. It should accept a ``policy`` - argument and pass it verbatim to the ``__init__`` method of - :class:`ParentWithSetFactory` together with the set of constraints; +- *inherit* from :class:`ParentWithSetFactory`. It should accept a + ``policy`` argument and pass it verbatim to the ``__init__`` method + of :class:`ParentWithSetFactory` together with the set of + constraints; -- *create its elements* trough calls to the method ``_element_constructor_``; +- *create its elements* trough calls to the method + ``_element_constructor_``; -- *define a method* :class:`ParentWithSetFactory.check_element` which checks if a - built element indeed belongs to it. The method should accept an extra - keyword parameter called ``check`` specifying which level of check should be - performed. It will only be called when ``bool(check)`` evaluates to - ``True``. +- *define a method* :class:`ParentWithSetFactory.check_element` which + checks if a built element indeed belongs to it. The method should + accept an extra keyword parameter called ``check`` specifying which + level of check should be performed. It will only be called when + ``bool(check)`` evaluates to ``True``. The constructor of the elements of a parent from a factory should: @@ -241,8 +246,8 @@ - accept an extra optional keyword parameter called ``check`` which is meant to tell if the input must be checked or not. The precise meaning of - ``check`` is intensionally left vague. The only intend is that if - ``bool(check)`` evaluates to ``False`` no check is performed at all. + ``check`` is intentionally left vague. The only intend is that if + ``bool(check)`` evaluates to ``False``, no check is performed at all. A factory should @@ -291,13 +296,25 @@ class SetFactory(UniqueRepresentation, SageObject): r""" This class is currently just a stub we will be using to add more structures on factories. + + TESTS:: + + sage: from sage.structure.set_factories import SetFactory + sage: S = SetFactory() + sage: S.__call__("foo") + Traceback (most recent call last): + ... + NotImplementedError: + sage: S.add_constraints("foo") + Traceback (most recent call last): + ... + NotImplementedError: """ @abstract_method def __call__(self, *constraints, **consdict): r""" - Construct the parent associated with the constraints - - Should return a :class:`Parent`. + Construct the parent associated with the constraints in + argument. This should return a :class:`Parent`. Currently there is no specification on how constraints are passed as arguments. @@ -329,9 +346,8 @@ def __call__(self, *constraints, **consdict): @abstract_method def add_constraints(self, cons, *args, **opts): r""" - Add constraints to the set cons - - Should return a set of constraints. Currently there is no + Add constraints to the set of constraints `cons`. Should + return a set of constraints. Currently there is no specification on how constraints are passed as arguments. EXAMPLES:: @@ -353,18 +369,19 @@ def add_constraints(self, cons, *args, **opts): NotImplementedError: """ - # TODO ? default policy + # TODO : default policy ? #################################################### # Policies # #################################################### + class SetFactoryPolicy(UniqueRepresentation, SageObject): r""" Abstract base class for policies. A policy is a device which is passed to a parent inheriting from - :class:`ParentWithSetFactory` in order to set-up the element construction - framework. + :class:`ParentWithSetFactory` in order to set-up the element + construction framework. INPUT: @@ -387,7 +404,7 @@ def __init__(self, factory): def factory(self): r""" - Return the factory for ``self`` + Returns the factory for ``self``. EXAMPLES:: @@ -410,9 +427,8 @@ def factory(self): def _self_element_constructor_attributes(self, Element): r""" - Element Constructor Attributes for non facade parent - - The list of attribute with must be set during the init of a non facade + Element Constructor Attributes for non facade parent. The list + of attributes with must be set during the init of a non facade parent with factory. INPUT:: @@ -431,10 +447,9 @@ def _self_element_constructor_attributes(self, Element): def _facade_element_constructor_attributes(self, parent): r""" - Element Constructor Attributes for facade parent - - The list of attribute with must be set during the init of a facade - parent with factory. + Element Constructor Attributes for facade parent. The list of + attribute with must be set during the init of a facade parent + with factory. INPUT:: @@ -454,7 +469,7 @@ def _facade_element_constructor_attributes(self, parent): @abstract_method def _element_constructor_attributes(self, constraints): r""" - Element constructor attributes + Element constructor attributes. INPUT: @@ -465,7 +480,7 @@ def _element_constructor_attributes(self, constraints): :meth:`ParentWithSetFactory._element_constructor_`. Currently to standard attributes are provided in :meth:`_facade_element_constructor_attributes` and - :meth:`_self_element_constructor_attributes`. You should returns to + :meth:`_self_element_constructor_attributes`. You should return to one needed depending on the given constraints. EXAMPLES:: @@ -480,15 +495,16 @@ def _element_constructor_attributes(self, constraints): class SelfParentPolicy(SetFactoryPolicy): r""" - Policy where each parent is a standard parent + Policy where each parent is a standard parent. INPUT: - ``factory`` -- an instance of :class:`SetFactory` - ``Element`` -- a subclass of :class:`~.element.Element` - Given a factory ``F`` an a class ``E``, returns a policy for parent ``P`` - creating element in class ``E`` and parent ``P`` itself. + Given a factory ``F`` an a class ``E``, returns a policy for + parent ``P`` creating elements in class ``E`` and parent ``P`` + itself. EXAMPLES:: @@ -552,7 +568,7 @@ def _repr_(self): class TopMostParentPolicy(SetFactoryPolicy): r""" - Policy where the parent of the element is the topmost parent + Policy where the parent of the elements is the topmost parent. INPUT: @@ -560,8 +576,9 @@ class TopMostParentPolicy(SetFactoryPolicy): - ``top_constraints`` -- the empty set of constraints. - ``Element`` -- a subclass of :class:`~.element.Element` - Given a factory ``F`` and a class ``E``, returns a policy for parent ``P`` - creating element in class ``E`` and parent ``factory(*top_constraints, policy)``. + Given a factory ``F`` and a class ``E``, returns a policy for + parent ``P`` creating element in class ``E`` and parent + ``factory(*top_constraints, policy)``. EXAMPLES:: @@ -587,7 +604,7 @@ def __init__(self, factory, top_constraints, Element): def _element_constructor_attributes(self, constraints): r""" Returns the element constructor attributes as per - :meth:`SetFactoryPolicy._element_constructor_attributes` + :meth:`SetFactoryPolicy._element_constructor_attributes`. INPUT: @@ -625,15 +642,16 @@ def _repr_(self): class FacadeParentPolicy(SetFactoryPolicy): r""" - Policy for facade parent + Policy for facade parent. INPUT: - ``factory`` -- an instance of :class:`SetFactory` - ``parent`` -- an instance of :class:`Parent` - Given a factory ``F`` an a class ``E``, returns a policy for parent ``P`` - creating element as if they were created by ``parent`` + Given a factory ``F`` an a class ``E``, returns a policy for + parent ``P`` creating elements as if they were created by + ``parent``. EXAMPLES:: @@ -652,8 +670,8 @@ class FacadeParentPolicy(SetFactoryPolicy): sage: type(el) is P.element_class True - If ``parent`` is itself a facade parent, then transitivity is correctly - applied:: + If ``parent`` is itself a facade parent, then transitivity is + correctly applied:: sage: P = XYPairs() sage: P2 = XYPairs(x=2) @@ -682,9 +700,7 @@ def __init__(self, factory, parent): def category(self, constraints): r""" - Return the policy category for given constraints - - Return the policy category associated to ``self`` for parent + Returns the policy category associated to ``self`` for parent constructed with the given constraints as per :meth:`SetFactoryPolicy.category`. Here constraints are ignored. @@ -702,13 +718,12 @@ def category(self, constraints): sage: from sage.structure.set_factories import * sage: F = FacadeParentPolicy(XYPairs, XYPairs()) """ - # assert(isinstance(constraints, tuple)) return FacadeParentPolicyCategory(self._parent_for) def _element_constructor_attributes(self, constraints): r""" Returns the element constructor attributes as per - :meth:`SetFactoryPolicy._element_constructor_attributes` + :meth:`SetFactoryPolicy._element_constructor_attributes`. INPUT: @@ -746,7 +761,7 @@ def _repr_(self): class ParentWithSetFactory(Parent): r""" - Abstract class for parent belonging to a set factory + Abstract class for parent belonging to a set factory. INPUT: @@ -754,8 +769,8 @@ class ParentWithSetFactory(Parent): - ``policy`` -- the policy for element construction - ``category`` -- the category of the parent (default to ``None``) - Depending on the constraints and the policy, initialize the parent in a - proper category to set up element construction. + Depending on the constraints and the policy, initialize the parent + in a proper category to set up element construction. EXAMPLES:: @@ -776,12 +791,10 @@ def __init__(self, constraints, policy, category = None): sage: isinstance(XYPairs(3), ParentWithSetFactory) # indirect doctest True """ - # assert(isinstance(constraints, tuple)) self._constraints = constraints assert(isinstance(policy, SetFactoryPolicy)) self._policy = policy policy_attributes = policy._element_constructor_attributes(constraints) - # print self._constraints, policy_attributes for attrname, attr in policy_attributes.items(): if attr == "self": setattr(self, attrname, self) @@ -797,9 +810,8 @@ def __init__(self, constraints, policy, category = None): def constraints(self): r""" - Return the constraints for ``self`` - - Currently there is no specification on how constraints are handled. + Returns the constraints defining ``self``. Currently there is + no specification on how constraints are handled. EXAMPLES:: @@ -815,7 +827,7 @@ def constraints(self): def policy(self): r""" - Return the policy used when ``self`` was created + Returns the policy used when ``self`` was created. EXAMPLES:: @@ -829,7 +841,7 @@ def policy(self): def facade_policy(self): r""" - Return the policy for parent facade for ``self`` + Returns the policy for parent facade for ``self``. EXAMPLES:: @@ -857,7 +869,7 @@ def facade_policy(self): def factory(self): r""" - Return the factory which built ``self`` + Returns the factory which built ``self``. EXAMPLES:: @@ -871,7 +883,7 @@ def factory(self): def subset(self, *args, **options): r""" - Return a subset of ``self`` by adding more constraints + Returns a subset of ``self`` by adding more constraints. EXAMPLES:: @@ -895,10 +907,9 @@ def subset(self, *args, **options): def _test_subset(self, **options): r""" - Tests that subsets with no extra parameters returns ``self`` - - Currently only test that one gets the same parent is no more - constraints are added. + Tests that subsets with no extra parameters returns + ``self``. Currently, only the test that one gets the same + parent when no more constraints, is performed. .. TODO:: @@ -911,14 +922,13 @@ def _test_subset(self, **options): sage: S = XYPairs() sage: S._test_subset() """ - #TODO verifie avec self.constraints tester = self._tester(**options) tester.assertTrue(self.subset() is self) @abstract_method def check_element(self, x, check): r""" - Check that ``x`` verifies the constraints of ``self`` + Check that ``x`` verifies the constraints of ``self``. INPUT: @@ -927,18 +937,21 @@ def check_element(self, x, check): - ``check`` -- the level of checking to be performed (usually a boolean). - This method may assume that ``x`` was properly constructed by ``self`` - or a possible super-set of ``self`` for which ``self`` is a facade. It - should return nothing is ``x`` verifies the constraints and raise a - :exc:`~exceptions.ValueError` explaining which constraints ``x`` - fails otherwise. + This method may assume that ``x`` was properly constructed by + ``self`` or a possible super-set of ``self`` for which + ``self`` is a facade. It should return nothing is ``x`` + verifies the constraints and raise a + :exc:`~exceptions.ValueError` explaining which constraints + ``x`` fails otherwise. + + The method should accept an extra parameter check specifying + which level of check should be performed. It will only be + called when ``bool(check)`` evaluates to ``True``. - The method should accept an extra parameter check specifying which - level of check should be performed. It will only be called when - ``bool(check)`` evaluates to ``True``. + .. TODO:: - .. TODO:: Should we always call check element and let it decide which - check has to be performed ? + Should we always call check element and let it decide + which check has to be performed ? EXAMPLES:: @@ -959,13 +972,13 @@ def check_element(self, x, check): def __contains__(self, x): r""" - Default implementation for ``__contains__``: + Default implementation for ``__contains__``. INPUT:: - ``x`` -- any object - Check for class, parent and calls ``self.check_element(x)`` + Check for class, parent and calls ``self.check_element(x)``. TESTS:: @@ -1051,4 +1064,3 @@ def _element_constructor_(self, *args, **keywords): if check: self.check_element(res, check) return res - diff --git a/src/sage/structure/set_factories_example.py b/src/sage/structure/set_factories_example.py index 79dd3a271b6..83fcc0bbc7a 100644 --- a/src/sage/structure/set_factories_example.py +++ b/src/sage/structure/set_factories_example.py @@ -32,13 +32,13 @@ class XYPairsFactory(SetFactory): r""" - An example of factory for sets of pairs of integers + An example of factory for sets of pairs of integers. .. SEEALSO:: :mod:`.set_factories` for an introduction to factories. """ def __call__(self, x=None, y=None, policy=None): r""" - Construct the subset from constraints + Construct the subset from constraints. Consider the set `S` of couple `(x,y)` with `x` and `y` in `I:=\{0,1,2,3,4\}`. Returns the subsets of element of `S` satisfying @@ -77,10 +77,12 @@ def __call__(self, x=None, y=None, policy=None): def add_constraints(self, cons, (args, opts)): r""" - Add constraints to the set ``cons`` as per :meth:`SetFactory.add_constraints <.set_factories.SetFactory.add_constraints>` + Add constraints to the set ``cons`` as per + :meth:`SetFactory.add_constraints<.set_factories.SetFactory.add_constraints>`. - This is a very crude implementation which ignore optional arguments. - They will be handled directly by the factory :func:`XYPair` + This is a very crude implementation which ignore optional + arguments. They will be handled directly by the factory + :func:`XYPair`. EXAMPLES:: @@ -136,7 +138,7 @@ def _repr_(self): class XYPair(ElementWrapper): r""" - A class for Element `(x,y)` with `x` and `y` in `\{0,1,2,3,4\}` + A class for Elements `(x,y)` with `x` and `y` in `\{0,1,2,3,4\}`. EXAMPLES:: @@ -167,8 +169,8 @@ def __init__(self, parent, value, check=True): class AllPairs(ParentWithSetFactory, DisjointUnionEnumeratedSets): r""" - This parent show how one can use set factories together with - :class:`DisjointUnionEnumeratedSets` + This parent shows how one can use set factories together with + :class:`DisjointUnionEnumeratedSets`. TESTS:: @@ -226,7 +228,7 @@ def check_element(self, el, check): class PairsX_(ParentWithSetFactory, UniqueRepresentation): r""" - The set of pair `(x, 0), (x, 1), ..., (x, 4)` + The set of pairs `(x, 0), (x, 1), ..., (x, 4)`. TESTS:: @@ -293,10 +295,13 @@ def __iter__(self): yield self._element_constructor_((self._x, i), check=False) - class Pairs_Y(ParentWithSetFactory, DisjointUnionEnumeratedSets): r""" - The set of pair `(0, y), (1, y), ..., (4, y)` + The set of pairs `(0, y), (1, y), ..., (4, y)`. + + .. warning:: + + Put a nice warning _singe_pair TESTS:: @@ -339,8 +344,10 @@ def an_element(self): """ return self._element_constructor_((0, self._y), check=False) - def _single_pair(self, letter): + def single_pair(self, letter): r""" + Comment that and put link to ducmentation caveat.... + TESTS:: sage: from sage.structure.set_factories_example import XYPairs From f82366701dbceee1c10637495b4fc30a15914ca5 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Fri, 11 Apr 2014 17:54:53 +0200 Subject: [PATCH 06/11] Minor doc improvements --- src/sage/structure/set_factories.py | 2 +- src/sage/structure/set_factories_example.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index b3cb0803bf2..cdae6d8cba1 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -939,7 +939,7 @@ def check_element(self, x, check): This method may assume that ``x`` was properly constructed by ``self`` or a possible super-set of ``self`` for which - ``self`` is a facade. It should return nothing is ``x`` + ``self`` is a facade. It should return nothing if ``x`` verifies the constraints and raise a :exc:`~exceptions.ValueError` explaining which constraints ``x`` fails otherwise. diff --git a/src/sage/structure/set_factories_example.py b/src/sage/structure/set_factories_example.py index 83fcc0bbc7a..6c0c5b9855d 100644 --- a/src/sage/structure/set_factories_example.py +++ b/src/sage/structure/set_factories_example.py @@ -80,9 +80,8 @@ def add_constraints(self, cons, (args, opts)): Add constraints to the set ``cons`` as per :meth:`SetFactory.add_constraints<.set_factories.SetFactory.add_constraints>`. - This is a very crude implementation which ignore optional - arguments. They will be handled directly by the factory - :func:`XYPair`. + This is a crude implementation for the sake of the demonstration which + should not be taken as an example. EXAMPLES:: From 0eeecd1c94859b2328a1487e4d5dae91b158e5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 15 Apr 2014 14:08:38 +0200 Subject: [PATCH 07/11] trac #10194 make sure that all test pass (and pyflakes and pep8 compliant) --- src/sage/structure/set_factories.py | 49 +++++++------- src/sage/structure/set_factories_example.py | 71 +++++++++++---------- 2 files changed, 64 insertions(+), 56 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index b3cb0803bf2..7598647b51c 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -65,7 +65,7 @@ sage: S.cardinality() 25 -Let's construct `S_2`, `S^3` and `S_2^3`:: +Let us construct `S_2`, `S^3` and `S_2^3`:: sage: Sx2 = XYPairs(x=2); Sx2.list() [(2, 0), (2, 1), (2, 2), (2, 3), (2, 4)] @@ -74,11 +74,11 @@ sage: S23 = XYPairs(x=2, y=3); S23.list() [(2, 3)] -Set factories provide an alternative way to build subsets of an already -constructed set: each set constructed by a factory has a method -:meth:`~ParentWithSetFactory.subset` which accept new constraints. Sets -constructed by the factory or the :meth:`~ParentWithSetFactory.subset` methods are -identical:: +Set factories provide an alternative way to build subsets of an +already constructed set: each set constructed by a factory has a +method :meth:`~ParentWithSetFactory.subset` which accept new +constraints. Sets constructed by the factory or the +:meth:`~ParentWithSetFactory.subset` methods are identical:: sage: Sx2s = S.subset(x=2); Sx2 is Sx2s True @@ -283,15 +283,14 @@ from sage.structure.sage_object import SageObject from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.categories.category import Category from sage.categories.sets_cat import Sets from sage.misc.abstract_method import abstract_method -from sage.misc.lazy_attribute import lazy_attribute #################################################### # Factories # #################################################### + class SetFactory(UniqueRepresentation, SageObject): r""" This class is currently just a stub we will be using to add more @@ -375,6 +374,7 @@ def add_constraints(self, cons, *args, **opts): # Policies # #################################################### + class SetFactoryPolicy(UniqueRepresentation, SageObject): r""" Abstract base class for policies. @@ -442,8 +442,7 @@ def _self_element_constructor_attributes(self, Element): sage: pol._self_element_constructor_attributes(XYPair) {'_parent_for': 'self', 'Element': } """ - return {'_parent_for' : "self", - 'Element' : Element} + return {'_parent_for': "self", 'Element': Element} def _facade_element_constructor_attributes(self, parent): r""" @@ -462,9 +461,9 @@ def _facade_element_constructor_attributes(self, parent): sage: pol._facade_element_constructor_attributes(XYPairs()) {'element_class': , '_facade_for': AllPairs, '_parent_for': AllPairs} """ - return {'_parent_for' : parent, - '_facade_for' : parent, - 'element_class' : parent.element_class} + return {'_parent_for': parent, + '_facade_for': parent, + 'element_class': parent.element_class} @abstract_method def _element_constructor_attributes(self, constraints): @@ -493,6 +492,7 @@ def _element_constructor_attributes(self, constraints): {'element_class': , '_facade_for': AllPairs, '_parent_for': AllPairs} """ + class SelfParentPolicy(SetFactoryPolicy): r""" Policy where each parent is a standard parent. @@ -564,7 +564,8 @@ def _repr_(self): sage: SelfParentPolicy(XYPairs, XYPair) # indirect doctest Set factory policy for with parent ``self`` """ - return "Set factory policy for %s with parent ``self``"%(self._Element) + return "Set factory policy for {} with parent ``self``".format(self._Element) + class TopMostParentPolicy(SetFactoryPolicy): r""" @@ -635,7 +636,7 @@ def _repr_(self): sage: TopMostParentPolicy(XYPairs, (), XYPair) # indirect doctest Set factory policy for with parent AllPairs[=Factory for XY pairs(())] """ - return "Set factory policy for %s with parent %s[=%s(%s)]"%( + return "Set factory policy for {} with parent {}[={}({})]".format( self._Element, self._factory(*self._top_constraints, policy=self), self._factory, self._top_constraints) @@ -751,7 +752,7 @@ def _repr_(self): sage: FacadeParentPolicy(XYPairs, XYPairs()) # indirect doctest Set factory policy for facade parent AllPairs """ - return "Set factory policy for facade parent %s"%( + return "Set factory policy for facade parent {}".format( self._parent_for) @@ -759,6 +760,7 @@ def _repr_(self): # Parent # #################################################### + class ParentWithSetFactory(Parent): r""" Abstract class for parent belonging to a set factory. @@ -782,7 +784,7 @@ class ParentWithSetFactory(Parent): sage: P.category() Category of facade finite enumerated sets """ - def __init__(self, constraints, policy, category = None): + def __init__(self, constraints, policy, category=None): r""" TESTS:: @@ -804,9 +806,8 @@ def __init__(self, constraints, policy, category = None): if '_facade_for' in attrname: category = Sets().Facades().or_subcategory(category) Parent.__init__(self, - category = Sets().or_subcategory(category), - facade = policy_attributes.get('_facade_for', None)) - + category=Sets().or_subcategory(category), + facade=policy_attributes.get('_facade_for', None)) def constraints(self): r""" @@ -903,7 +904,7 @@ def subset(self, *args, **options): factory = self.factory() constr = factory.add_constraints(self._constraints, (args, options)) - return factory(*constr, policy = self._policy) + return factory(*constr, policy=self._policy) def _test_subset(self, **options): r""" @@ -948,7 +949,7 @@ def check_element(self, x, check): which level of check should be performed. It will only be called when ``bool(check)`` evaluates to ``True``. - .. TODO:: + .. TODO:: Should we always call check element and let it decide which check has to be performed ? @@ -995,7 +996,7 @@ def __contains__(self, x): False """ if (isinstance(x, self.element_class) and - x.parent() == self._parent_for): # TODO: is_parent_of ??? + x.parent() == self._parent_for): # TODO: is_parent_of ??? try: self.check_element(x, True) except ValueError: @@ -1059,7 +1060,7 @@ def _element_constructor_(self, *args, **keywords): sage: XYPairs(x=3)((2,3), check=False) # Don't do this at home, kids (2, 3) """ - check = keywords.get("check", True) + check = keywords.get("check", True) res = self.element_class(self._parent_for, *args, **keywords) if check: self.check_element(res, check) diff --git a/src/sage/structure/set_factories_example.py b/src/sage/structure/set_factories_example.py index 83fcc0bbc7a..09f1f064405 100644 --- a/src/sage/structure/set_factories_example.py +++ b/src/sage/structure/set_factories_example.py @@ -30,6 +30,7 @@ MAX = 5 + class XYPairsFactory(SetFactory): r""" An example of factory for sets of pairs of integers. @@ -40,7 +41,7 @@ def __call__(self, x=None, y=None, policy=None): r""" Construct the subset from constraints. - Consider the set `S` of couple `(x,y)` with `x` and `y` in + Consider the set `S` of couples `(x,y)` with `x` and `y` in `I:=\{0,1,2,3,4\}`. Returns the subsets of element of `S` satisfying some constraints. @@ -68,12 +69,10 @@ def __call__(self, x=None, y=None, policy=None): if isinstance(x, (Integer, int)): if isinstance(y, (Integer, int)): return SingletonPair(x, y, policy) - else: - return PairsX_(x, policy) + return PairsX_(x, policy) elif isinstance(y, (Integer, int)): return Pairs_Y(y, policy) - else: - return AllPairs(policy) + return AllPairs(policy) def add_constraints(self, cons, (args, opts)): r""" @@ -97,15 +96,20 @@ def add_constraints(self, cons, (args, opts)): (2, 3) """ res = list(cons) - res += [None]*(2-len(res)) + res += [None] * (2 - len(res)) + def set_args(argss): for i, v in enumerate(argss): if res[i] is not None and v is not None: - raise ValueError, "Duplicate value for constraints '%s': was %s now %s"%( - ['x','y'][i], res[i], v) - if v is not None: res[i] = v + raise ValueError("Duplicate value for constraints '{}': " + "was {} now {}".format(['x', 'y'][i], + res[i], v)) + if v is not None: + res[i] = v set_args(args) - def parse_args(x=None,y=None): set_args((x,y)) + + def parse_args(x=None, y=None): + set_args((x, y)) parse_args(**opts) if res == (None, None): return () @@ -136,6 +140,7 @@ def _repr_(self): XYPairs = XYPairsFactory() XYPairs.__doc__ = XYPairsFactory.__call__.__doc__ + class XYPair(ElementWrapper): r""" A class for Elements `(x,y)` with `x` and `y` in `\{0,1,2,3,4\}`. @@ -160,13 +165,14 @@ def __init__(self, parent, value, check=True): """ if check: if not isinstance(value, tuple): - raise ValueError, "Value %s must be a tuple"%value + raise ValueError("Value {} must be a tuple".format(value)) if len(value) != 2: - raise ValueError, "Value must be of length 2" + raise ValueError("Value must be of length 2") if not all(int(x) in range(MAX) for x in value): - raise ValueError, "numbers must be in range(%s)"%MAX + raise ValueError("numbers must be in range({})".format(MAX)) ElementWrapper.__init__(self, parent, value) + class AllPairs(ParentWithSetFactory, DisjointUnionEnumeratedSets): r""" This parent shows how one can use set factories together with @@ -186,11 +192,11 @@ def __init__(self, policy): sage: TestSuite(XYPairs()).run() """ ParentWithSetFactory.__init__(self, (), policy, - category = FiniteEnumeratedSets()) + category=FiniteEnumeratedSets()) DisjointUnionEnumeratedSets.__init__( self, LazyFamily(range(MAX), self._single_pair), - facade=True, keepkey = False, - category = self.category()) + facade=True, keepkey=False, + category=self.category()) def _single_pair(self, letter): r""" @@ -226,6 +232,7 @@ def check_element(self, el, check): """ pass + class PairsX_(ParentWithSetFactory, UniqueRepresentation): r""" The set of pairs `(x, 0), (x, 1), ..., (x, 4)`. @@ -244,8 +251,8 @@ def __init__(self, x, policy): sage: TestSuite(XYPairs(0)).run() """ self._x = x - ParentWithSetFactory.__init__(self, (x,None), policy, - category = FiniteEnumeratedSets()) + ParentWithSetFactory.__init__(self, (x, None), policy, + category=FiniteEnumeratedSets()) def _repr_(self): """ @@ -255,7 +262,7 @@ def _repr_(self): sage: XYPairs(x=1) {(1, b) | b in range(5)} """ - return "{(%s, b) | b in range(%s)}"%(self._x, MAX) + return "{(%s, b) | b in range(%s)}" % (self._x, MAX) def an_element(self): r""" @@ -281,7 +288,7 @@ def check_element(self, el, check): """ (x, y) = el.value if x != self._x: - raise ValueError, "Wrong first coordinate" + raise ValueError("Wrong first coordinate") def __iter__(self): r""" @@ -299,9 +306,9 @@ class Pairs_Y(ParentWithSetFactory, DisjointUnionEnumeratedSets): r""" The set of pairs `(0, y), (1, y), ..., (4, y)`. - .. warning:: + .. warning:: - Put a nice warning _singe_pair + Put a nice warning _single_pair TESTS:: @@ -318,11 +325,11 @@ def __init__(self, y, policy): """ self._y = y ParentWithSetFactory.__init__(self, (None, y), policy, - category = FiniteEnumeratedSets()) + category=FiniteEnumeratedSets()) DisjointUnionEnumeratedSets.__init__( self, LazyFamily(range(MAX), self._single_pair), - facade=True, keepkey = False, - category = self.category()) # TODO remove and fix disjoint union. + facade=True, keepkey=False, + category=self.category()) # TODO remove and fix disjoint union. def _repr_(self): """ @@ -332,7 +339,7 @@ def _repr_(self): sage: XYPairs(y=1) {(a, 1) | a in range(5)} """ - return "{(a, %s) | a in range(%s)}"%(self._y, MAX) + return "{(a, %s) | a in range(%s)}" % (self._y, MAX) def an_element(self): r""" @@ -344,9 +351,9 @@ def an_element(self): """ return self._element_constructor_((0, self._y), check=False) - def single_pair(self, letter): + def _single_pair(self, letter): r""" - Comment that and put link to ducmentation caveat.... + Comment that and put link to documentation caveat.... TESTS:: @@ -370,7 +377,7 @@ def check_element(self, el, check): """ (x, y) = el.value if y != self._y: - raise ValueError, "Wrong second coordinate" + raise ValueError("Wrong second coordinate") class SingletonPair(ParentWithSetFactory, UniqueRepresentation): @@ -390,7 +397,7 @@ def __init__(self, x, y, policy): """ self._xy = (x, y) ParentWithSetFactory.__init__(self, (x, y), policy, - category = FiniteEnumeratedSets()) + category=FiniteEnumeratedSets()) def _repr_(self): """ @@ -400,7 +407,7 @@ def _repr_(self): sage: XYPairs(x=2, y=1) {(2, 1)} """ - return "{%s}"%(self._xy,) + return "{%s}" % (self._xy,) def check_element(self, el, check): r""" @@ -418,7 +425,7 @@ def check_element(self, el, check): ValueError: Wrong coordinate """ if el.value != self._xy: - raise ValueError, "Wrong coordinate" + raise ValueError("Wrong coordinate") def __iter__(self): r""" From 014d842b9917c3090619cc9ea6aac681dde5c0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 23 May 2014 22:12:23 +0200 Subject: [PATCH 08/11] trac #10194 details and formatting --- src/sage/structure/set_factories.py | 96 ++++++++++++--------- src/sage/structure/set_factories_example.py | 35 ++++---- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index 076d6b74200..95563c39734 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -4,7 +4,7 @@ A *set factory* `F` is a device for constructing some :class:`Parent` -`P` that model subsets of a big set `S`. Typically, each such parent +`P` that models subsets of a big set `S`. Typically, each such parent is constructed as the subset of `S` of all elements satisfying a certain collection of constraints `cons`. In such a hierarchy of subsets, one needs an easy and flexible control on how elements are @@ -31,8 +31,8 @@ written code is intentionally kept minimal, many things and in particular several iterators could be written in a more efficient way. -Consider the set `S` of couple `(x,y)` with `x` and `y` in `I:=\{0,1,2,3,4\}`. -We represent the element of `S` as 2-elements tuple, wrapped in a class +Consider the set `S` of couples `(x,y)` with `x` and `y` in `I:=\{0,1,2,3,4\}`. +We represent an element of `S` as a 2-elements tuple, wrapped in a class :class:`~.set_factories_example.XYPair` deriving from :class:`ElementWrapper`. You can create a :class:`~.set_factories_example.XYPair` with any :class:`Parent`:: @@ -55,8 +55,8 @@ example, there would be much more of them. And for some sets of constraints no good enumeration algorithms would be known. -In Sage, those sets are constructed passing the constraints to the factory. We -first create the set with no constraints at all:: +In Sage, those sets are constructed by passing the constraints to the +factory. We first create the set with no constraints at all:: sage: XYPairs Factory for XY pairs @@ -96,8 +96,8 @@ We now come to the point of factories: constructing custom elements. By default, the writer of :func:`~.set_factories_example.XYPairs` decided that -the parent ``Sx2``, ``Sy3`` and ``S23`` are facade for parent ``S``. This -means that each elements constructed by those subsets behaves as if they where +the parents ``Sx2``, ``Sy3`` and ``S23`` are facade for parent ``S``. This +means that each element constructed by those subsets behaves as if they where directly constructed by ``S`` istelf:: sage: Sx2.an_element().parent() @@ -127,15 +127,15 @@ `x+y`. We therefore inherit from :class:`~.set_factories_example.XYPair`:: sage: class NewXYPair(XYPair): - ... def sum(self): - ... return sum(self.value) + ....: def sum(self): + ....: return sum(self.value) sage: el = NewXYPair(Parent(), (2,3)) sage: el.sum() 5 We now want to have subsets generating those new elements while still having a single real parent (the one with no constraint) for each element. The -corresponding policy is called :class:`TopMostParentPolicy`. It takes tree +corresponding policy is called :class:`TopMostParentPolicy`. It takes three parameters: - the factory; @@ -146,7 +146,7 @@ elements with the new policy:: sage: newpolicy = TopMostParentPolicy(XYPairs, (), NewXYPair) - sage: newS = XYPairs(policy = newpolicy) + sage: newS = XYPairs(policy=newpolicy) sage: el = newS.an_element(); el (0, 0) sage: el.sum() @@ -174,12 +174,12 @@ Here is an examples:: sage: selfpolicy = SelfParentPolicy(XYPairs, NewXYPair) - sage: selfS = XYPairs(policy = selfpolicy) + sage: selfS = XYPairs(policy=selfpolicy) sage: el = selfS.an_element(); sage: el.parent() is selfS True -Now subsets are parent of the element they create:: +Now all subsets are the parent of the elements that they create:: sage: selfS2 = selfS.subset(x=2) sage: el2 = selfS2.an_element() @@ -199,7 +199,7 @@ ownership of all elements and the class which will be use for the ``Element``. -- :class:`SelfParentPolicy`: provide systematically Element and +- :class:`SelfParentPolicy`: provide systematically ``Element`` and element_class and ensure that the parent is ``self``. .. TODO:: Generalize :class:`TopMostParentPolicy` to be able to have several @@ -207,14 +207,14 @@ .. RUBRIC:: Technicalities: how policies inform parents -Parent built from factories should inherits from +Parents built from factories should inherit from :class:`ParentWithSetFactory`. This class provide a few methods related to factories and policies. The ``__init__`` method of :class:`ParentWithSetFactory` must be provided with the set of constraints and the policy. A parent built from a factory must creates -elements trough a call to the method ``_element_constructor_``. The +elements through a call to the method ``_element_constructor_``. The current way policies inform parents how to builds their elements is -setted by a few attributes. So the class must accept attribute +set by a few attributes. So the class must accept attribute adding. The precise details of which attribute are set may be subject to change in the future. @@ -231,7 +231,7 @@ of :class:`ParentWithSetFactory` together with the set of constraints; -- *create its elements* trough calls to the method +- *create its elements* through calls to the method ``_element_constructor_``; - *define a method* :class:`ParentWithSetFactory.check_element` which @@ -246,7 +246,7 @@ - accept an extra optional keyword parameter called ``check`` which is meant to tell if the input must be checked or not. The precise meaning of - ``check`` is intentionally left vague. The only intend is that if + ``check`` is intentionally left vague. The only intent is that if ``bool(check)`` evaluates to ``False``, no check is performed at all. A factory should @@ -293,8 +293,8 @@ class SetFactory(UniqueRepresentation, SageObject): r""" - This class is currently just a stub we will be using to add more - structures on factories. + This class is currently just a stub that we will be using to add + more structures on factories. TESTS:: @@ -315,8 +315,8 @@ def __call__(self, *constraints, **consdict): Construct the parent associated with the constraints in argument. This should return a :class:`Parent`. - Currently there is no specification on how constraints are passed as - arguments. + Currently there is no specification on how constraints are + passed as arguments. EXAMPLES:: @@ -345,8 +345,9 @@ def __call__(self, *constraints, **consdict): @abstract_method def add_constraints(self, cons, *args, **opts): r""" - Add constraints to the set of constraints `cons`. Should - return a set of constraints. Currently there is no + Add constraints to the set of constraints `cons`. + + Should return a set of constraints. Currently there is no specification on how constraints are passed as arguments. EXAMPLES:: @@ -427,11 +428,12 @@ def factory(self): def _self_element_constructor_attributes(self, Element): r""" - Element Constructor Attributes for non facade parent. The list - of attributes with must be set during the init of a non facade - parent with factory. + Element Constructor Attributes for non facade parent. + + The list of attributes which must be set during the init of a + non facade parent with factory. - INPUT:: + INPUT: - ``Element`` -- the class used for the elements @@ -446,11 +448,12 @@ def _self_element_constructor_attributes(self, Element): def _facade_element_constructor_attributes(self, parent): r""" - Element Constructor Attributes for facade parent. The list of - attribute with must be set during the init of a facade parent - with factory. + Element Constructor Attributes for facade parent. - INPUT:: + The list of attributes which must be set during the init of a + facade parent with factory. + + INPUT: - ``parent`` -- the actual parent for the elements @@ -474,9 +477,9 @@ def _element_constructor_attributes(self, constraints): - ``constraints`` -- a bunch of constraints - Should returns the attributes that are prerequisite for element + Should return the attributes that are prerequisite for element construction. This is coordinated with - :meth:`ParentWithSetFactory._element_constructor_`. Currently to standard + :meth:`ParentWithSetFactory._element_constructor_`. Currently two standard attributes are provided in :meth:`_facade_element_constructor_attributes` and :meth:`_self_element_constructor_attributes`. You should return to @@ -502,7 +505,7 @@ class SelfParentPolicy(SetFactoryPolicy): - ``factory`` -- an instance of :class:`SetFactory` - ``Element`` -- a subclass of :class:`~.element.Element` - Given a factory ``F`` an a class ``E``, returns a policy for + Given a factory ``F`` and a class ``E``, returns a policy for parent ``P`` creating elements in class ``E`` and parent ``P`` itself. @@ -717,7 +720,10 @@ def category(self, constraints): sage: from sage.structure.set_factories_example import * sage: from sage.structure.set_factories import * - sage: F = FacadeParentPolicy(XYPairs, XYPairs()) + sage: F = FacadeParentPolicy(XYPairs, XYPairs()); F + Set factory policy for facade parent AllPairs + sage: F.category(()) + NameError: global name 'FacadeParentPolicyCategory' is not defined """ return FacadeParentPolicyCategory(self._parent_for) @@ -811,8 +817,10 @@ def __init__(self, constraints, policy, category=None): def constraints(self): r""" - Returns the constraints defining ``self``. Currently there is - no specification on how constraints are handled. + Returns the constraints defining ``self``. + + Currently there is no specification on how constraints are + handled. EXAMPLES:: @@ -909,8 +917,10 @@ def subset(self, *args, **options): def _test_subset(self, **options): r""" Tests that subsets with no extra parameters returns - ``self``. Currently, only the test that one gets the same - parent when no more constraints, is performed. + ``self``. + + Currently, only the test that one gets the same parent when no + more constraints are given, is performed. .. TODO:: @@ -975,7 +985,7 @@ def __contains__(self, x): r""" Default implementation for ``__contains__``. - INPUT:: + INPUT: - ``x`` -- any object @@ -1040,7 +1050,7 @@ def __call__(self, *args, **keywords): # QUESTION: Should we call: # self._parent_for._element_constructor_ - # currently we don't call it directly because: + # currently we do not call it directly because: # - it may do some extra check we dont want to perform ? # - calling directly element_class should be faster def _element_constructor_(self, *args, **keywords): diff --git a/src/sage/structure/set_factories_example.py b/src/sage/structure/set_factories_example.py index 166f5db26eb..4006892e95c 100644 --- a/src/sage/structure/set_factories_example.py +++ b/src/sage/structure/set_factories_example.py @@ -2,14 +2,13 @@ An example of set factory ========================= -The goal of this module is to exemplify the use of set factories. Note that -the written code is intentionally kept minimal; many things and in particular -several iterators could be written in a more efficient way. +The goal of this module is to exemplify the use of set factories. Note +that the code is intentionally kept minimal; many things and in +particular several iterators could be written in a more efficient way. .. SEEALSO:: :mod:`.set_factories` for an introduction to set factories, their specifications, and examples of their use and implementation based on this module. - """ #***************************************************************************** # Copyright (C) 2012 Florent Hivert @@ -33,9 +32,9 @@ class XYPairsFactory(SetFactory): r""" - An example of factory for sets of pairs of integers. + An example of set factory, for sets of pairs of integers. - .. SEEALSO:: :mod:`.set_factories` for an introduction to factories. + .. SEEALSO:: :mod:`.set_factories` for an introduction to set factories. """ def __call__(self, x=None, y=None, policy=None): r""" @@ -53,10 +52,15 @@ def __call__(self, x=None, y=None, policy=None): .. SEEALSO:: :class:`.set_factories.SetFactoryPolicy` - EXAMPLES:: + EXAMPLES: + + Let us first create the set factory:: sage: from sage.structure.set_factories_example import XYPairsFactory sage: XYPairs = XYPairsFactory() + + One can then use the set factory to construct a set:: + sage: P = XYPairs(); P.list() [(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)] @@ -191,11 +195,12 @@ def __init__(self, policy): sage: TestSuite(XYPairs()).run() """ ParentWithSetFactory.__init__(self, (), policy, - category=FiniteEnumeratedSets()) - DisjointUnionEnumeratedSets.__init__( - self, LazyFamily(range(MAX), self._single_pair), - facade=True, keepkey=False, - category=self.category()) + category=FiniteEnumeratedSets()) + DisjointUnionEnumeratedSets.__init__(self, + LazyFamily(range(MAX), + self._single_pair), + facade=True, keepkey=False, + category=self.category()) def _single_pair(self, letter): r""" @@ -251,7 +256,7 @@ def __init__(self, x, policy): """ self._x = x ParentWithSetFactory.__init__(self, (x, None), policy, - category=FiniteEnumeratedSets()) + category=FiniteEnumeratedSets()) def _repr_(self): """ @@ -324,7 +329,7 @@ def __init__(self, y, policy): """ self._y = y ParentWithSetFactory.__init__(self, (None, y), policy, - category=FiniteEnumeratedSets()) + category=FiniteEnumeratedSets()) DisjointUnionEnumeratedSets.__init__( self, LazyFamily(range(MAX), self._single_pair), facade=True, keepkey=False, @@ -396,7 +401,7 @@ def __init__(self, x, y, policy): """ self._xy = (x, y) ParentWithSetFactory.__init__(self, (x, y), policy, - category=FiniteEnumeratedSets()) + category=FiniteEnumeratedSets()) def _repr_(self): """ From e061484adfe23b2b8c2a5d0a1fafe4cff1de2b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 10 Jul 2014 21:12:46 +0200 Subject: [PATCH 09/11] trac #10194 removed the category method (was useless and wrong ?) --- src/sage/structure/set_factories.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index 95563c39734..874ae023939 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -702,31 +702,6 @@ def __init__(self, factory, parent): self._parent_for = parent SetFactoryPolicy.__init__(self, factory) - def category(self, constraints): - r""" - Returns the policy category associated to ``self`` for parent - constructed with the given constraints as per - :meth:`SetFactoryPolicy.category`. Here constraints are ignored. - - INPUT: - - - ``constraints`` -- a set of constraints (ignored) - - OUTPUT: - - - an instance of :class:`FacadeParentPolicyCategory`. - - EXAMPLE:: - - sage: from sage.structure.set_factories_example import * - sage: from sage.structure.set_factories import * - sage: F = FacadeParentPolicy(XYPairs, XYPairs()); F - Set factory policy for facade parent AllPairs - sage: F.category(()) - NameError: global name 'FacadeParentPolicyCategory' is not defined - """ - return FacadeParentPolicyCategory(self._parent_for) - def _element_constructor_attributes(self, constraints): r""" Returns the element constructor attributes as per From 641c28adea6a0ff81aeab6cf4a928e8c45613d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 26 Aug 2014 21:49:46 +0200 Subject: [PATCH 10/11] trac #10194 minor doc changes --- src/sage/structure/set_factories.py | 35 ++++++++++++--------- src/sage/structure/set_factories_example.py | 20 ++++++++---- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index 874ae023939..bd8acbc652c 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -202,7 +202,9 @@ - :class:`SelfParentPolicy`: provide systematically ``Element`` and element_class and ensure that the parent is ``self``. -.. TODO:: Generalize :class:`TopMostParentPolicy` to be able to have several +.. TODO:: + + Generalize :class:`TopMostParentPolicy` to be able to have several topmost parent. .. RUBRIC:: Technicalities: how policies inform parents @@ -261,12 +263,13 @@ the :class:`ParentWithSetFactory` if no policy is given to the factory. -.. TODO:: There is currently no support for dealing with sets of +.. TODO:: + + There is currently no support for dealing with sets of constraints. The set factory and the parents must cooperate to consistently handle them. More support, together with a generic mechanism to select the appropriate parent class from the constraints, will be added - shortly to sage, as soon as we have gathered sufficiently enough - use-cases. + as soon as we have gathered sufficiently enough use-cases. AUTHORS: @@ -388,8 +391,10 @@ class SetFactoryPolicy(UniqueRepresentation, SageObject): - ``factory`` -- a :class:`SetFactory` - .. WARNING:: This class is a base class for policies, one should not try - to create instances. + .. WARNING:: + + This class is a base class for policies, one should not try + to create instances. """ def __init__(self, factory): r""" @@ -405,7 +410,7 @@ def __init__(self, factory): def factory(self): r""" - Returns the factory for ``self``. + Return the factory for ``self``. EXAMPLES:: @@ -541,7 +546,7 @@ def __init__(self, factory, Element): def _element_constructor_attributes(self, constraints): r""" - Returns the element constructor attributes as per + Return the element constructor attributes as per :meth:`SetFactoryPolicy._element_constructor_attributes` INPUT: @@ -607,7 +612,7 @@ def __init__(self, factory, top_constraints, Element): def _element_constructor_attributes(self, constraints): r""" - Returns the element constructor attributes as per + Return the element constructor attributes as per :meth:`SetFactoryPolicy._element_constructor_attributes`. INPUT: @@ -704,7 +709,7 @@ def __init__(self, factory, parent): def _element_constructor_attributes(self, constraints): r""" - Returns the element constructor attributes as per + Return the element constructor attributes as per :meth:`SetFactoryPolicy._element_constructor_attributes`. INPUT: @@ -792,7 +797,7 @@ def __init__(self, constraints, policy, category=None): def constraints(self): r""" - Returns the constraints defining ``self``. + Return the constraints defining ``self``. Currently there is no specification on how constraints are handled. @@ -811,7 +816,7 @@ def constraints(self): def policy(self): r""" - Returns the policy used when ``self`` was created. + Return the policy used when ``self`` was created. EXAMPLES:: @@ -825,7 +830,7 @@ def policy(self): def facade_policy(self): r""" - Returns the policy for parent facade for ``self``. + Return the policy for parent facade for ``self``. EXAMPLES:: @@ -853,7 +858,7 @@ def facade_policy(self): def factory(self): r""" - Returns the factory which built ``self``. + Return the factory which built ``self``. EXAMPLES:: @@ -867,7 +872,7 @@ def factory(self): def subset(self, *args, **options): r""" - Returns a subset of ``self`` by adding more constraints. + Return a subset of ``self`` by adding more constraints. EXAMPLES:: diff --git a/src/sage/structure/set_factories_example.py b/src/sage/structure/set_factories_example.py index 4006892e95c..5c33b0649ca 100644 --- a/src/sage/structure/set_factories_example.py +++ b/src/sage/structure/set_factories_example.py @@ -6,7 +6,9 @@ that the code is intentionally kept minimal; many things and in particular several iterators could be written in a more efficient way. -.. SEEALSO:: :mod:`.set_factories` for an introduction to set +.. SEEALSO:: + + :mod:`.set_factories` for an introduction to set factories, their specifications, and examples of their use and implementation based on this module. """ @@ -34,7 +36,9 @@ class XYPairsFactory(SetFactory): r""" An example of set factory, for sets of pairs of integers. - .. SEEALSO:: :mod:`.set_factories` for an introduction to set factories. + .. SEEALSO:: + + :mod:`.set_factories` for an introduction to set factories. """ def __call__(self, x=None, y=None, policy=None): r""" @@ -50,7 +54,9 @@ def __call__(self, x=None, y=None, policy=None): - ``y=b`` -- where ``b`` is an integer (default to ``None``). - ``policy`` -- the policy passed to the created set. - .. SEEALSO:: :class:`.set_factories.SetFactoryPolicy` + .. SEEALSO:: + + :class:`.set_factories.SetFactoryPolicy` EXAMPLES: @@ -64,8 +70,10 @@ def __call__(self, x=None, y=None, policy=None): sage: P = XYPairs(); P.list() [(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)] - .. note:: This function is actually the ``__call__`` method of - :class:`XYPairsFactory`. + .. NOTE:: + + This function is actually the ``__call__`` method of + :class:`XYPairsFactory`. """ if policy is None: policy = self._default_policy @@ -310,7 +318,7 @@ class Pairs_Y(ParentWithSetFactory, DisjointUnionEnumeratedSets): r""" The set of pairs `(0, y), (1, y), ..., (4, y)`. - .. warning:: + .. WARNING:: Put a nice warning _single_pair From ed7f340783a1850d309af930f2fab698295482b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 25 Sep 2014 21:28:52 +0200 Subject: [PATCH 11/11] trac #10194 found a few typo again --- src/sage/structure/set_factories.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/structure/set_factories.py b/src/sage/structure/set_factories.py index bd8acbc652c..bf8d4d2f1cd 100644 --- a/src/sage/structure/set_factories.py +++ b/src/sage/structure/set_factories.py @@ -98,7 +98,7 @@ default, the writer of :func:`~.set_factories_example.XYPairs` decided that the parents ``Sx2``, ``Sy3`` and ``S23`` are facade for parent ``S``. This means that each element constructed by those subsets behaves as if they where -directly constructed by ``S`` istelf:: +directly constructed by ``S`` itself:: sage: Sx2.an_element().parent() AllPairs @@ -171,7 +171,7 @@ - the factory; - the class used for elements. -Here is an examples:: +Here is an example:: sage: selfpolicy = SelfParentPolicy(XYPairs, NewXYPair) sage: selfS = XYPairs(policy=selfpolicy) @@ -196,7 +196,7 @@ - :class:`TopMostParentPolicy`: use a parent created by the factory itself and provide a class ``Element`` for it. In this case, we need to specify the set of constraints which build this parent taking the - ownership of all elements and the class which will be use for the + ownership of all elements and the class which will be used for the ``Element``. - :class:`SelfParentPolicy`: provide systematically ``Element`` and @@ -205,7 +205,7 @@ .. TODO:: Generalize :class:`TopMostParentPolicy` to be able to have several - topmost parent. + topmost parents. .. RUBRIC:: Technicalities: how policies inform parents @@ -213,11 +213,11 @@ :class:`ParentWithSetFactory`. This class provide a few methods related to factories and policies. The ``__init__`` method of :class:`ParentWithSetFactory` must be provided with the set of -constraints and the policy. A parent built from a factory must creates +constraints and the policy. A parent built from a factory must create elements through a call to the method ``_element_constructor_``. The -current way policies inform parents how to builds their elements is +current way in which policies inform parents how to builds their elements is set by a few attributes. So the class must accept attribute -adding. The precise details of which attribute are set may be subject +adding. The precise details of which attributes are set may be subject to change in the future. .. RUBRIC:: How to write a set factory @@ -658,7 +658,7 @@ class FacadeParentPolicy(SetFactoryPolicy): - ``factory`` -- an instance of :class:`SetFactory` - ``parent`` -- an instance of :class:`Parent` - Given a factory ``F`` an a class ``E``, returns a policy for + Given a factory ``F`` and a class ``E``, returns a policy for parent ``P`` creating elements as if they were created by ``parent``. @@ -993,8 +993,7 @@ def __contains__(self, x): return False else: return True - else: - return False + return False def __call__(self, *args, **keywords): r"""