From d60ba478bee7e0746f7e7fcc059ba9c876fc88fa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 11 Dec 2023 18:33:41 -0800 Subject: [PATCH 1/9] sage.sets.family: Cythonize --- src/sage/combinat/root_system/type_relabel.py | 8 +-- src/sage/sets/family.pxd | 10 ++++ src/sage/sets/{family.py => family.pyx} | 60 ++++++++++--------- 3 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 src/sage/sets/family.pxd rename src/sage/sets/{family.py => family.pyx} (97%) diff --git a/src/sage/combinat/root_system/type_relabel.py b/src/sage/combinat/root_system/type_relabel.py index 350e290b32a..712937d08ce 100644 --- a/src/sage/combinat/root_system/type_relabel.py +++ b/src/sage/combinat/root_system/type_relabel.py @@ -10,7 +10,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute -from sage.sets.family import FiniteFamily +from sage.sets.family import Family, FiniteFamily from sage.combinat.root_system import cartan_type from sage.combinat.root_system import ambient_space from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations @@ -173,10 +173,10 @@ def __init__(self, type, relabelling): sage: rI5 = CartanType(['I',5]).relabel({1:0,2:1}) sage: rI5.root_system().ambient_space() """ - assert isinstance(relabelling, FiniteFamily) cartan_type.CartanType_decorator.__init__(self, type) - self._relabelling = relabelling._dictionary - self._relabelling_inverse = relabelling.inverse_family()._dictionary + relabelling = Family(relabelling) + self._relabelling = dict(relabelling.items()) + self._relabelling_inverse = dict(relabelling.inverse_family().items()) self._index_set = tuple(sorted(relabelling[i] for i in type.index_set())) # TODO: design an appropriate infrastructure to handle this # automatically? Maybe using categories and axioms? diff --git a/src/sage/sets/family.pxd b/src/sage/sets/family.pxd new file mode 100644 index 00000000000..c18b81538cd --- /dev/null +++ b/src/sage/sets/family.pxd @@ -0,0 +1,10 @@ +from sage.structure.parent cimport Parent + + +cdef class AbstractFamily(Parent): + cdef public __custom_name + + +cdef class FiniteFamily(AbstractFamily): + cdef dict _dictionary + cdef object _keys diff --git a/src/sage/sets/family.py b/src/sage/sets/family.pyx similarity index 97% rename from src/sage/sets/family.py rename to src/sage/sets/family.pyx index f3c3d0a7556..17498b7f895 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.pyx @@ -38,18 +38,17 @@ from pprint import pformat, saferepr from collections.abc import Iterable -from sage.misc.abstract_method import abstract_method -from sage.misc.cachefunc import cached_method -from sage.structure.parent import Parent from sage.categories.enumerated_sets import EnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from sage.misc.cachefunc import cached_method +from sage.misc.call import AttrCallObject from sage.misc.lazy_import import lazy_import +from sage.rings.infinity import Infinity from sage.rings.integer import Integer -from sage.misc.call import AttrCallObject +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.sets.non_negative_integers import NonNegativeIntegers -from sage.rings.infinity import Infinity + lazy_import('sage.combinat.combinat', 'CombinatorialClass') @@ -396,8 +395,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa return TrivialFamily(indices) if isinstance(indices, (FiniteFamily, LazyFamily, TrivialFamily)): return indices - if (indices in EnumeratedSets() - or isinstance(indices, CombinatorialClass)): + if indices in EnumeratedSets(): return EnumeratedFamily(indices) if isinstance(indices, Iterable): return TrivialFamily(indices) @@ -418,7 +416,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa keys=indices) -class AbstractFamily(Parent): +cdef class AbstractFamily(Parent): """ The abstract class for family @@ -436,7 +434,6 @@ def hidden_keys(self): """ return [] - @abstract_method def keys(self): """ Return the keys of the family. @@ -447,8 +444,8 @@ def keys(self): sage: sorted(f.keys()) [3, 4, 7] """ + raise NotImplementedError - @abstract_method(optional=True) def values(self): """ Return the elements (values) of this family. @@ -459,6 +456,7 @@ def values(self): sage: sorted(f.values()) ['aa', 'bb', 'cc'] """ + raise NotImplementedError def items(self): """ @@ -535,7 +533,8 @@ def inverse_family(self): return Family({self[k]: k for k in self.keys()}) -class FiniteFamily(AbstractFamily): + +cdef class FiniteFamily(AbstractFamily): r""" A :class:`FiniteFamily` is an associative container which models a finite family `(f_i)_{i \in I}`. Its elements `f_i` are therefore its @@ -712,8 +711,8 @@ def __eq__(self, other): False """ return (isinstance(other, self.__class__) and - self._keys == other._keys and - self._dictionary == other._dictionary) + self._keys == ( other)._keys and + self._dictionary == ( other)._dictionary) def _repr_(self): """ @@ -829,6 +828,7 @@ def __setstate__(self, state): self.__init__(state['dictionary'], keys=state.get("keys")) + class FiniteFamilyWithHiddenKeys(FiniteFamily): r""" A close variant of :class:`FiniteFamily` where the family contains some @@ -869,15 +869,17 @@ def __getitem__(self, i): ... KeyError """ - if i in self._dictionary: - return self._dictionary[i] - - if i not in self.hidden_dictionary: - if i not in self._hidden_keys: - raise KeyError - self.hidden_dictionary[i] = self.hidden_function(i) - - return self.hidden_dictionary[i] + try: + return super().__getitem__(i) + except KeyError: + try: + return self.hidden_dictionary[i] + except KeyError: + if i not in self._hidden_keys: + raise KeyError + v = self.hidden_function(i) + self.hidden_dictionary[i] = v + return v def hidden_keys(self): """ @@ -902,11 +904,11 @@ def __getstate__(self): """ from sage.misc.fpickle import pickle_function f = pickle_function(self.hidden_function) - return {'dictionary': self._dictionary, - 'hidden_keys': self._hidden_keys, - 'hidden_dictionary': self.hidden_dictionary, - 'hidden_function': f, - 'keys': self._keys} + state = super().__getstate__() + state.update({'hidden_keys': self._hidden_keys, + 'hidden_dictionary': self.hidden_dictionary, + 'hidden_function': f}) + return state def __setstate__(self, d): """ @@ -1074,7 +1076,7 @@ def _repr_(self): """ if self.function_name is not None: name = self.function_name + "(i)" - elif isinstance(self.function, type(lambda x: 1)): + elif isinstance(self.function, types.LambdaType): name = self.function.__name__ name = name + "(i)" else: From bcd8dfc35686fd48cd7c1db4eedc26a6dbfa0afc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 3 Jan 2023 14:58:24 -0800 Subject: [PATCH 2/9] MIPVariable: Make it a subclass of FiniteFamily --- src/sage/numerical/mip.pxd | 7 ++++--- src/sage/numerical/mip.pyx | 35 +++++++++++++++++------------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/sage/numerical/mip.pxd b/src/sage/numerical/mip.pxd index 612324a5424..e73cc408d09 100644 --- a/src/sage/numerical/mip.pxd +++ b/src/sage/numerical/mip.pxd @@ -3,8 +3,11 @@ cdef extern from *: cdef int REAL = -1 cdef int INTEGER = 0 +from sage.sets.family cimport FiniteFamily from sage.structure.sage_object cimport SageObject from sage.numerical.backends.generic_backend cimport GenericBackend + + cdef class MIPVariable @@ -26,10 +29,8 @@ cdef class MixedIntegerLinearProgram(SageObject): cpdef sum(self, L) noexcept -cdef class MIPVariable(SageObject): +cdef class MIPVariable(FiniteFamily): cdef MixedIntegerLinearProgram _p - cdef dict _dict - cdef bint _dynamic_indices cdef int _vtype cdef str _name cdef object _lower_bound diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 1e6e6caa7bd..803724bf29b 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -3237,7 +3237,7 @@ class MIPSolverException(RuntimeError): pass -cdef class MIPVariable(SageObject): +cdef class MIPVariable(FiniteFamily): r""" ``MIPVariable`` is a variable used by the class ``MixedIntegerLinearProgram``. @@ -3286,17 +3286,16 @@ cdef class MIPVariable(SageObject): MIPVariable with 0 real components, >= 0 """ - self._dict = {} + super().__init__({}) self._p = mip self._vtype = vtype self._lower_bound = lower_bound self._upper_bound = upper_bound self._name = name - self._dynamic_indices = True if indices is not None: for i in indices: self[i] # creates component - self._dynamic_indices = False + self._keys = indices def __copy__(self): r""" @@ -3398,9 +3397,9 @@ cdef class MIPVariable(SageObject): """ cdef int j - if i in self._dict: - return self._dict[i] - if not self._dynamic_indices: + if i in self._dictionary: + return self._dictionary[i] + if self._keys is not None: raise IndexError("{} does not index a component of {}".format(i, self)) zero = self._p._backend.zero() name = self._name + "[" + str(i) + "]" if self._name else None @@ -3415,7 +3414,7 @@ cdef class MIPVariable(SageObject): name=name) v = self._p.linear_functions_parent()({j : 1}) self._p._variables[v] = j - self._dict[i] = v + self._dictionary[i] = v return v def copy_for_mip(self, mip): @@ -3461,8 +3460,8 @@ cdef class MIPVariable(SageObject): """ cdef MIPVariable cp = type(self)(mip, self._vtype, self._name, self._lower_bound, self._upper_bound) - cp._dict = copy(self._dict) - cp._dynamic_indices = self._dynamic_indices + cp._dictionary = copy(self._dictionary) + cp._keys = self._keys return cp def set_min(self, min): @@ -3501,7 +3500,7 @@ cdef class MIPVariable(SageObject): """ self._lower_bound = min - for v in self._dict.values(): + for v in self._dictionary.values(): self._p.set_min(v,min) def set_max(self, max): @@ -3537,7 +3536,7 @@ cdef class MIPVariable(SageObject): True """ self._upper_bound = max - for v in self._dict.values(): + for v in self._dictionary.values(): self._p.set_max(v,max) def _repr_(self): @@ -3570,9 +3569,9 @@ cdef class MIPVariable(SageObject): """ s = 'MIPVariable{0} with {1} {2} component{3}'.format( " " + self._name if self._name else "", - len(self._dict), + len(self._dictionary), {0:"binary", -1:"real", 1:"integer"}[self._vtype], - "s" if len(self._dict) != 1 else "") + "s" if len(self._dictionary) != 1 else "") if (self._vtype != 0) and (self._lower_bound is not None): s += ', >= {0}'.format(self._lower_bound) if (self._vtype != 0) and (self._upper_bound is not None): @@ -3591,11 +3590,11 @@ cdef class MIPVariable(SageObject): sage: sorted(v.keys()) [0, 1] """ - return self._dict.keys() + return self._dictionary.keys() def items(self): r""" - Return the pairs (keys,value) contained in the dictionary. + Return the pairs (keys, value) contained in the dictionary. EXAMPLES:: @@ -3605,7 +3604,7 @@ cdef class MIPVariable(SageObject): sage: sorted(v.items()) [(0, x_0), (1, x_1)] """ - return self._dict.items() + return self._dictionary.items() def values(self): r""" @@ -3619,7 +3618,7 @@ cdef class MIPVariable(SageObject): sage: sorted(v.values(), key=str) [x_0, x_1] """ - return self._dict.values() + return self._dictionary.values() def mip(self): r""" From 59442ad810d66183f3e55b728173277d4cba2a12 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 11 Dec 2023 18:34:13 -0800 Subject: [PATCH 3/9] src/sage/sets/family.py: Update copyright according to git blame -w --date=format:%Y FILE | sort -k2 --- src/sage/sets/family.pyx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/sets/family.pyx b/src/sage/sets/family.pyx index 17498b7f895..9f7429f5ec2 100644 --- a/src/sage/sets/family.pyx +++ b/src/sage/sets/family.pyx @@ -22,10 +22,16 @@ Check :trac:`12482` (shall be run in a fresh session):: Category of finite enumerated sets """ -# **************************************************************************** -# Copyright (C) 2008 Nicolas Thiery , -# Mike Hansen , -# Florent Hivert +# ***************************************************************************** +# Copyright (C) 2008-2017 Nicolas Thiery +# 2008-2009 Mike Hansen +# 2008-2010 Florent Hivert +# 2013-2021 Travis Scrimshaw +# 2014 Nathann Cohen +# 2017 Erik M. Bray +# 2018 Frédéric Chapoton +# 2019 Markus Wageringel +# 2022 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 1405fac242a861664015bb730ba653ea2eaf5bfe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 11 Dec 2023 18:35:09 -0800 Subject: [PATCH 4/9] sage.sets.family: Fix pickling --- src/sage/sets/family.pyx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/sets/family.pyx b/src/sage/sets/family.pyx index 9f7429f5ec2..14968a259b0 100644 --- a/src/sage/sets/family.pyx +++ b/src/sage/sets/family.pyx @@ -31,7 +31,7 @@ Check :trac:`12482` (shall be run in a fresh session):: # 2017 Erik M. Bray # 2018 Frédéric Chapoton # 2019 Markus Wageringel -# 2022 Matthias Koeppe +# 2022-2023 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -834,7 +834,6 @@ cdef class FiniteFamily(AbstractFamily): self.__init__(state['dictionary'], keys=state.get("keys")) - class FiniteFamilyWithHiddenKeys(FiniteFamily): r""" A close variant of :class:`FiniteFamily` where the family contains some @@ -876,7 +875,7 @@ class FiniteFamilyWithHiddenKeys(FiniteFamily): KeyError """ try: - return super().__getitem__(i) + return FiniteFamily.__getitem__(self, i) except KeyError: try: return self.hidden_dictionary[i] @@ -930,7 +929,7 @@ class FiniteFamilyWithHiddenKeys(FiniteFamily): 6 """ hidden_function = d['hidden_function'] - if isinstance(hidden_function, str): + if isinstance(hidden_function, (str, bytes)): # Let's assume that hidden_function is an unpickled function. from sage.misc.fpickle import unpickle_function hidden_function = unpickle_function(hidden_function) From 6cea5084a5f0cadf987a880fb1d511b01b4c3a5f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 3 Jan 2023 18:59:34 -0800 Subject: [PATCH 5/9] src/sage/sets/family.pxd: Make attributes public --- src/sage/sets/family.pxd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/family.pxd b/src/sage/sets/family.pxd index c18b81538cd..43f4fc9fd1e 100644 --- a/src/sage/sets/family.pxd +++ b/src/sage/sets/family.pxd @@ -6,5 +6,5 @@ cdef class AbstractFamily(Parent): cdef class FiniteFamily(AbstractFamily): - cdef dict _dictionary - cdef object _keys + cdef public dict _dictionary + cdef public object _keys From 4ef3f8f05e313e8413fc4da391224edb897514c3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 11 Dec 2023 18:35:55 -0800 Subject: [PATCH 6/9] Make FiniteFamily a Python subclass of FiniteFamily_base - so that EnumeratedSets mixins work correctly --- src/sage/numerical/mip.pxd | 4 +- src/sage/numerical/mip.pyx | 2 +- src/sage/sets/family.pxd | 2 +- src/sage/sets/family.pyx | 89 ++++++++++++++++++++------------------ 4 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/sage/numerical/mip.pxd b/src/sage/numerical/mip.pxd index e73cc408d09..4bc1ccc963a 100644 --- a/src/sage/numerical/mip.pxd +++ b/src/sage/numerical/mip.pxd @@ -3,7 +3,7 @@ cdef extern from *: cdef int REAL = -1 cdef int INTEGER = 0 -from sage.sets.family cimport FiniteFamily +from sage.sets.family cimport FiniteFamily_base from sage.structure.sage_object cimport SageObject from sage.numerical.backends.generic_backend cimport GenericBackend @@ -29,7 +29,7 @@ cdef class MixedIntegerLinearProgram(SageObject): cpdef sum(self, L) noexcept -cdef class MIPVariable(FiniteFamily): +cdef class MIPVariable(FiniteFamily_base): cdef MixedIntegerLinearProgram _p cdef int _vtype cdef str _name diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 803724bf29b..9b212953329 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -3237,7 +3237,7 @@ class MIPSolverException(RuntimeError): pass -cdef class MIPVariable(FiniteFamily): +cdef class MIPVariable(FiniteFamily_base): r""" ``MIPVariable`` is a variable used by the class ``MixedIntegerLinearProgram``. diff --git a/src/sage/sets/family.pxd b/src/sage/sets/family.pxd index 43f4fc9fd1e..fd170f2f641 100644 --- a/src/sage/sets/family.pxd +++ b/src/sage/sets/family.pxd @@ -5,6 +5,6 @@ cdef class AbstractFamily(Parent): cdef public __custom_name -cdef class FiniteFamily(AbstractFamily): +cdef class FiniteFamily_base(AbstractFamily): cdef public dict _dictionary cdef public object _keys diff --git a/src/sage/sets/family.pyx b/src/sage/sets/family.pyx index 14968a259b0..0e562d94bb9 100644 --- a/src/sage/sets/family.pyx +++ b/src/sage/sets/family.pyx @@ -540,48 +540,10 @@ cdef class AbstractFamily(Parent): -cdef class FiniteFamily(AbstractFamily): +cdef class FiniteFamily_base(AbstractFamily): r""" - A :class:`FiniteFamily` is an associative container which models a finite - family `(f_i)_{i \in I}`. Its elements `f_i` are therefore its - values. Instances should be created via the :func:`Family` factory. See its - documentation for examples and tests. - - EXAMPLES: - - We define the family `(f_i)_{i \in \{3,4,7\}}` with `f_3=a`, - `f_4=b`, and `f_7=d`:: - - sage: from sage.sets.family import FiniteFamily - sage: f = FiniteFamily({3: 'a', 4: 'b', 7: 'd'}) - - Individual elements are accessible as in a usual dictionary:: - - sage: f[7] - 'd' - - And the other usual dictionary operations are also available:: - - sage: len(f) - 3 - sage: f.keys() - [3, 4, 7] - - However f behaves as a container for the `f_i`'s:: - - sage: list(f) - ['a', 'b', 'd'] - sage: [ x for x in f ] - ['a', 'b', 'd'] - - The order of the elements can be specified using the ``keys`` optional argument:: - - sage: f = FiniteFamily({"a": "aa", "b": "bb", "c" : "cc" }, keys = ["c", "a", "b"]) - sage: list(f) - ['cc', 'aa', 'bb'] - + Cython base class for :class:`FiniteFamily`. """ - def __init__(self, dictionary, keys=None): """ TESTS:: @@ -717,8 +679,8 @@ cdef class FiniteFamily(AbstractFamily): False """ return (isinstance(other, self.__class__) and - self._keys == ( other)._keys and - self._dictionary == ( other)._dictionary) + self._keys == ( other)._keys and + self._dictionary == ( other)._dictionary) def _repr_(self): """ @@ -834,6 +796,49 @@ cdef class FiniteFamily(AbstractFamily): self.__init__(state['dictionary'], keys=state.get("keys")) +class FiniteFamily(FiniteFamily_base): + r""" + A :class:`FiniteFamily` is an associative container which models a finite + family `(f_i)_{i \in I}`. Its elements `f_i` are therefore its + values. Instances should be created via the :func:`Family` factory. See its + documentation for examples and tests. + + EXAMPLES: + + We define the family `(f_i)_{i \in \{3,4,7\}}` with `f_3=a`, + `f_4=b`, and `f_7=d`:: + + sage: from sage.sets.family import FiniteFamily + sage: f = FiniteFamily({3: 'a', 4: 'b', 7: 'd'}) + + Individual elements are accessible as in a usual dictionary:: + + sage: f[7] + 'd' + + And the other usual dictionary operations are also available:: + + sage: len(f) + 3 + sage: f.keys() + [3, 4, 7] + + However f behaves as a container for the `f_i`'s:: + + sage: list(f) + ['a', 'b', 'd'] + sage: [ x for x in f ] + ['a', 'b', 'd'] + + The order of the elements can be specified using the ``keys`` optional argument:: + + sage: f = FiniteFamily({"a": "aa", "b": "bb", "c" : "cc" }, keys = ["c", "a", "b"]) + sage: list(f) + ['cc', 'aa', 'bb'] + + """ + pass + class FiniteFamilyWithHiddenKeys(FiniteFamily): r""" A close variant of :class:`FiniteFamily` where the family contains some From 91d04f4a63e04561e65ac1261d72fdb24af80989 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 11 Dec 2023 18:36:22 -0800 Subject: [PATCH 7/9] Revert "Make FiniteFamily a Python subclass of FiniteFamily_base - so that EnumeratedSets mixins work correctly" This reverts commit 450f9c63e05cae3661f147c6bb54b9137cf15bf6. --- src/sage/numerical/mip.pxd | 4 +- src/sage/numerical/mip.pyx | 2 +- src/sage/sets/family.pxd | 2 +- src/sage/sets/family.pyx | 89 ++++++++++++++++++-------------------- 4 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/sage/numerical/mip.pxd b/src/sage/numerical/mip.pxd index 4bc1ccc963a..e73cc408d09 100644 --- a/src/sage/numerical/mip.pxd +++ b/src/sage/numerical/mip.pxd @@ -3,7 +3,7 @@ cdef extern from *: cdef int REAL = -1 cdef int INTEGER = 0 -from sage.sets.family cimport FiniteFamily_base +from sage.sets.family cimport FiniteFamily from sage.structure.sage_object cimport SageObject from sage.numerical.backends.generic_backend cimport GenericBackend @@ -29,7 +29,7 @@ cdef class MixedIntegerLinearProgram(SageObject): cpdef sum(self, L) noexcept -cdef class MIPVariable(FiniteFamily_base): +cdef class MIPVariable(FiniteFamily): cdef MixedIntegerLinearProgram _p cdef int _vtype cdef str _name diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 9b212953329..803724bf29b 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -3237,7 +3237,7 @@ class MIPSolverException(RuntimeError): pass -cdef class MIPVariable(FiniteFamily_base): +cdef class MIPVariable(FiniteFamily): r""" ``MIPVariable`` is a variable used by the class ``MixedIntegerLinearProgram``. diff --git a/src/sage/sets/family.pxd b/src/sage/sets/family.pxd index fd170f2f641..43f4fc9fd1e 100644 --- a/src/sage/sets/family.pxd +++ b/src/sage/sets/family.pxd @@ -5,6 +5,6 @@ cdef class AbstractFamily(Parent): cdef public __custom_name -cdef class FiniteFamily_base(AbstractFamily): +cdef class FiniteFamily(AbstractFamily): cdef public dict _dictionary cdef public object _keys diff --git a/src/sage/sets/family.pyx b/src/sage/sets/family.pyx index 0e562d94bb9..14968a259b0 100644 --- a/src/sage/sets/family.pyx +++ b/src/sage/sets/family.pyx @@ -540,10 +540,48 @@ cdef class AbstractFamily(Parent): -cdef class FiniteFamily_base(AbstractFamily): +cdef class FiniteFamily(AbstractFamily): r""" - Cython base class for :class:`FiniteFamily`. + A :class:`FiniteFamily` is an associative container which models a finite + family `(f_i)_{i \in I}`. Its elements `f_i` are therefore its + values. Instances should be created via the :func:`Family` factory. See its + documentation for examples and tests. + + EXAMPLES: + + We define the family `(f_i)_{i \in \{3,4,7\}}` with `f_3=a`, + `f_4=b`, and `f_7=d`:: + + sage: from sage.sets.family import FiniteFamily + sage: f = FiniteFamily({3: 'a', 4: 'b', 7: 'd'}) + + Individual elements are accessible as in a usual dictionary:: + + sage: f[7] + 'd' + + And the other usual dictionary operations are also available:: + + sage: len(f) + 3 + sage: f.keys() + [3, 4, 7] + + However f behaves as a container for the `f_i`'s:: + + sage: list(f) + ['a', 'b', 'd'] + sage: [ x for x in f ] + ['a', 'b', 'd'] + + The order of the elements can be specified using the ``keys`` optional argument:: + + sage: f = FiniteFamily({"a": "aa", "b": "bb", "c" : "cc" }, keys = ["c", "a", "b"]) + sage: list(f) + ['cc', 'aa', 'bb'] + """ + def __init__(self, dictionary, keys=None): """ TESTS:: @@ -679,8 +717,8 @@ cdef class FiniteFamily_base(AbstractFamily): False """ return (isinstance(other, self.__class__) and - self._keys == ( other)._keys and - self._dictionary == ( other)._dictionary) + self._keys == ( other)._keys and + self._dictionary == ( other)._dictionary) def _repr_(self): """ @@ -796,49 +834,6 @@ cdef class FiniteFamily_base(AbstractFamily): self.__init__(state['dictionary'], keys=state.get("keys")) -class FiniteFamily(FiniteFamily_base): - r""" - A :class:`FiniteFamily` is an associative container which models a finite - family `(f_i)_{i \in I}`. Its elements `f_i` are therefore its - values. Instances should be created via the :func:`Family` factory. See its - documentation for examples and tests. - - EXAMPLES: - - We define the family `(f_i)_{i \in \{3,4,7\}}` with `f_3=a`, - `f_4=b`, and `f_7=d`:: - - sage: from sage.sets.family import FiniteFamily - sage: f = FiniteFamily({3: 'a', 4: 'b', 7: 'd'}) - - Individual elements are accessible as in a usual dictionary:: - - sage: f[7] - 'd' - - And the other usual dictionary operations are also available:: - - sage: len(f) - 3 - sage: f.keys() - [3, 4, 7] - - However f behaves as a container for the `f_i`'s:: - - sage: list(f) - ['a', 'b', 'd'] - sage: [ x for x in f ] - ['a', 'b', 'd'] - - The order of the elements can be specified using the ``keys`` optional argument:: - - sage: f = FiniteFamily({"a": "aa", "b": "bb", "c" : "cc" }, keys = ["c", "a", "b"]) - sage: list(f) - ['cc', 'aa', 'bb'] - - """ - pass - class FiniteFamilyWithHiddenKeys(FiniteFamily): r""" A close variant of :class:`FiniteFamily` where the family contains some From 9e21551480145fbb6cd3ad538029b2751c13c570 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 7 Jan 2023 11:14:37 -0800 Subject: [PATCH 8/9] FiniteFamily: Simpler solution that makes EnumeratedSets mixins work correctly --- src/sage/sets/family.pxd | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/sets/family.pxd b/src/sage/sets/family.pxd index 43f4fc9fd1e..f5d8f755ecc 100644 --- a/src/sage/sets/family.pxd +++ b/src/sage/sets/family.pxd @@ -3,6 +3,7 @@ from sage.structure.parent cimport Parent cdef class AbstractFamily(Parent): cdef public __custom_name + cdef dict __dict__ # enables Python attributes as needed for EnumeratedSets() cdef class FiniteFamily(AbstractFamily): From 889f36a01aa7026e3a6abe47f131d25e28ccc5ad Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 11 Dec 2023 20:45:05 -0800 Subject: [PATCH 9/9] src/sage/sets/family.pyx: Use LazyImport --- src/sage/sets/family.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/family.pyx b/src/sage/sets/family.pyx index 14968a259b0..d1c102261a8 100644 --- a/src/sage/sets/family.pyx +++ b/src/sage/sets/family.pyx @@ -49,13 +49,13 @@ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.misc.cachefunc import cached_method from sage.misc.call import AttrCallObject -from sage.misc.lazy_import import lazy_import +from sage.misc.lazy_import import LazyImport from sage.rings.infinity import Infinity from sage.rings.integer import Integer from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.sets.non_negative_integers import NonNegativeIntegers -lazy_import('sage.combinat.combinat', 'CombinatorialClass') +CombinatorialClass = LazyImport('sage.combinat.combinat', 'CombinatorialClass') def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=False, name=None):