Skip to content

Commit 2017233

Browse files
author
Release Manager
committed
gh-35121: `sage.sets.family`: Cythonize; change `MIPVariable` to a subclass of `FiniteFamily` <!-- ^^^^^ Please provide a concise, informative and self-explanatory title. Don't put issue numbers in there, do this in the PR body below. For example, instead of "Fixes #1234" use "Introduce new method to calculate 1+1" --> ### 📚 Description Fixes #31750 <!-- Describe your changes here in detail --> <!-- Why is this change required? What problem does it solve? --> <!-- If it resolves an open issue, please link to the issue here. For example "Closes #1337" --> ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> <!-- If your change requires a documentation PR, please link it appropriately --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> - [ ] I have made sure that the title is self-explanatory and the description concisely explains the PR. - [ ] I have linked an issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open pull requests that this PR logically depends on --> <!-- - #xyz: short description why this is a dependency - #abc: ... --> URL: #35121 Reported by: Matthias Köppe Reviewer(s): Travis Scrimshaw
2 parents e2e0f8d + 889f36a commit 2017233

File tree

5 files changed

+79
-61
lines changed

5 files changed

+79
-61
lines changed

src/sage/combinat/root_system/type_relabel.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from sage.misc.cachefunc import cached_method
1212
from sage.misc.lazy_attribute import lazy_attribute
13-
from sage.sets.family import FiniteFamily
13+
from sage.sets.family import Family, FiniteFamily
1414
from sage.combinat.root_system import cartan_type
1515
from sage.combinat.root_system import ambient_space
1616
from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations
@@ -173,10 +173,10 @@ def __init__(self, type, relabelling):
173173
sage: rI5 = CartanType(['I',5]).relabel({1:0,2:1})
174174
sage: rI5.root_system().ambient_space()
175175
"""
176-
assert isinstance(relabelling, FiniteFamily)
177176
cartan_type.CartanType_decorator.__init__(self, type)
178-
self._relabelling = relabelling._dictionary
179-
self._relabelling_inverse = relabelling.inverse_family()._dictionary
177+
relabelling = Family(relabelling)
178+
self._relabelling = dict(relabelling.items())
179+
self._relabelling_inverse = dict(relabelling.inverse_family().items())
180180
self._index_set = tuple(sorted(relabelling[i] for i in type.index_set()))
181181
# TODO: design an appropriate infrastructure to handle this
182182
# automatically? Maybe using categories and axioms?

src/sage/numerical/mip.pxd

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ cdef extern from *:
33
cdef int REAL = -1
44
cdef int INTEGER = 0
55

6+
from sage.sets.family cimport FiniteFamily
67
from sage.structure.sage_object cimport SageObject
78
from sage.numerical.backends.generic_backend cimport GenericBackend
9+
10+
811
cdef class MIPVariable
912

1013

@@ -26,10 +29,8 @@ cdef class MixedIntegerLinearProgram(SageObject):
2629
cpdef sum(self, L) noexcept
2730

2831

29-
cdef class MIPVariable(SageObject):
32+
cdef class MIPVariable(FiniteFamily):
3033
cdef MixedIntegerLinearProgram _p
31-
cdef dict _dict
32-
cdef bint _dynamic_indices
3334
cdef int _vtype
3435
cdef str _name
3536
cdef object _lower_bound

src/sage/numerical/mip.pyx

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,7 +3237,7 @@ class MIPSolverException(RuntimeError):
32373237
pass
32383238

32393239

3240-
cdef class MIPVariable(SageObject):
3240+
cdef class MIPVariable(FiniteFamily):
32413241
r"""
32423242
``MIPVariable`` is a variable used by the class
32433243
``MixedIntegerLinearProgram``.
@@ -3286,17 +3286,16 @@ cdef class MIPVariable(SageObject):
32863286
MIPVariable with 0 real components, >= 0
32873287
32883288
"""
3289-
self._dict = {}
3289+
super().__init__({})
32903290
self._p = mip
32913291
self._vtype = vtype
32923292
self._lower_bound = lower_bound
32933293
self._upper_bound = upper_bound
32943294
self._name = name
3295-
self._dynamic_indices = True
32963295
if indices is not None:
32973296
for i in indices:
32983297
self[i] # creates component
3299-
self._dynamic_indices = False
3298+
self._keys = indices
33003299

33013300
def __copy__(self):
33023301
r"""
@@ -3398,9 +3397,9 @@ cdef class MIPVariable(SageObject):
33983397
33993398
"""
34003399
cdef int j
3401-
if i in self._dict:
3402-
return self._dict[i]
3403-
if not self._dynamic_indices:
3400+
if i in self._dictionary:
3401+
return self._dictionary[i]
3402+
if self._keys is not None:
34043403
raise IndexError("{} does not index a component of {}".format(i, self))
34053404
zero = self._p._backend.zero()
34063405
name = self._name + "[" + str(i) + "]" if self._name else None
@@ -3415,7 +3414,7 @@ cdef class MIPVariable(SageObject):
34153414
name=name)
34163415
v = self._p.linear_functions_parent()({j : 1})
34173416
self._p._variables[v] = j
3418-
self._dict[i] = v
3417+
self._dictionary[i] = v
34193418
return v
34203419

34213420
def copy_for_mip(self, mip):
@@ -3461,8 +3460,8 @@ cdef class MIPVariable(SageObject):
34613460
"""
34623461
cdef MIPVariable cp = type(self)(mip, self._vtype, self._name,
34633462
self._lower_bound, self._upper_bound)
3464-
cp._dict = copy(self._dict)
3465-
cp._dynamic_indices = self._dynamic_indices
3463+
cp._dictionary = copy(self._dictionary)
3464+
cp._keys = self._keys
34663465
return cp
34673466

34683467
def set_min(self, min):
@@ -3501,7 +3500,7 @@ cdef class MIPVariable(SageObject):
35013500
35023501
"""
35033502
self._lower_bound = min
3504-
for v in self._dict.values():
3503+
for v in self._dictionary.values():
35053504
self._p.set_min(v,min)
35063505

35073506
def set_max(self, max):
@@ -3537,7 +3536,7 @@ cdef class MIPVariable(SageObject):
35373536
True
35383537
"""
35393538
self._upper_bound = max
3540-
for v in self._dict.values():
3539+
for v in self._dictionary.values():
35413540
self._p.set_max(v,max)
35423541

35433542
def _repr_(self):
@@ -3570,9 +3569,9 @@ cdef class MIPVariable(SageObject):
35703569
"""
35713570
s = 'MIPVariable{0} with {1} {2} component{3}'.format(
35723571
" " + self._name if self._name else "",
3573-
len(self._dict),
3572+
len(self._dictionary),
35743573
{0:"binary", -1:"real", 1:"integer"}[self._vtype],
3575-
"s" if len(self._dict) != 1 else "")
3574+
"s" if len(self._dictionary) != 1 else "")
35763575
if (self._vtype != 0) and (self._lower_bound is not None):
35773576
s += ', >= {0}'.format(self._lower_bound)
35783577
if (self._vtype != 0) and (self._upper_bound is not None):
@@ -3591,11 +3590,11 @@ cdef class MIPVariable(SageObject):
35913590
sage: sorted(v.keys())
35923591
[0, 1]
35933592
"""
3594-
return self._dict.keys()
3593+
return self._dictionary.keys()
35953594

35963595
def items(self):
35973596
r"""
3598-
Return the pairs (keys,value) contained in the dictionary.
3597+
Return the pairs (keys, value) contained in the dictionary.
35993598
36003599
EXAMPLES::
36013600
@@ -3605,7 +3604,7 @@ cdef class MIPVariable(SageObject):
36053604
sage: sorted(v.items())
36063605
[(0, x_0), (1, x_1)]
36073606
"""
3608-
return self._dict.items()
3607+
return self._dictionary.items()
36093608

36103609
def values(self):
36113610
r"""
@@ -3619,7 +3618,7 @@ cdef class MIPVariable(SageObject):
36193618
sage: sorted(v.values(), key=str)
36203619
[x_0, x_1]
36213620
"""
3622-
return self._dict.values()
3621+
return self._dictionary.values()
36233622

36243623
def mip(self):
36253624
r"""

src/sage/sets/family.pxd

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from sage.structure.parent cimport Parent
2+
3+
4+
cdef class AbstractFamily(Parent):
5+
cdef public __custom_name
6+
cdef dict __dict__ # enables Python attributes as needed for EnumeratedSets()
7+
8+
9+
cdef class FiniteFamily(AbstractFamily):
10+
cdef public dict _dictionary
11+
cdef public object _keys

src/sage/sets/family.py renamed to src/sage/sets/family.pyx

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,16 @@
2222
Category of finite enumerated sets
2323
"""
2424

25-
# ****************************************************************************
26-
# Copyright (C) 2008 Nicolas Thiery <nthiery at users.sf.net>,
27-
# Mike Hansen <[email protected]>,
28-
# Florent Hivert <[email protected]>
25+
# *****************************************************************************
26+
# Copyright (C) 2008-2017 Nicolas Thiery <nthiery at users.sf.net>
27+
# 2008-2009 Mike Hansen <[email protected]>
28+
# 2008-2010 Florent Hivert <[email protected]>
29+
# 2013-2021 Travis Scrimshaw
30+
# 2014 Nathann Cohen
31+
# 2017 Erik M. Bray
32+
# 2018 Frédéric Chapoton
33+
# 2019 Markus Wageringel
34+
# 2022-2023 Matthias Koeppe
2935
#
3036
# This program is free software: you can redistribute it and/or modify
3137
# it under the terms of the GNU General Public License as published by
@@ -38,19 +44,18 @@
3844
from pprint import pformat, saferepr
3945
from collections.abc import Iterable
4046

41-
from sage.misc.abstract_method import abstract_method
42-
from sage.misc.cachefunc import cached_method
43-
from sage.structure.parent import Parent
4447
from sage.categories.enumerated_sets import EnumeratedSets
4548
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
4649
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
47-
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
48-
from sage.misc.lazy_import import lazy_import
49-
from sage.rings.integer import Integer
50+
from sage.misc.cachefunc import cached_method
5051
from sage.misc.call import AttrCallObject
51-
from sage.sets.non_negative_integers import NonNegativeIntegers
52+
from sage.misc.lazy_import import LazyImport
5253
from sage.rings.infinity import Infinity
53-
lazy_import('sage.combinat.combinat', 'CombinatorialClass')
54+
from sage.rings.integer import Integer
55+
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
56+
from sage.sets.non_negative_integers import NonNegativeIntegers
57+
58+
CombinatorialClass = LazyImport('sage.combinat.combinat', 'CombinatorialClass')
5459

5560

5661
def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=False, name=None):
@@ -396,8 +401,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa
396401
return TrivialFamily(indices)
397402
if isinstance(indices, (FiniteFamily, LazyFamily, TrivialFamily)):
398403
return indices
399-
if (indices in EnumeratedSets()
400-
or isinstance(indices, CombinatorialClass)):
404+
if indices in EnumeratedSets():
401405
return EnumeratedFamily(indices)
402406
if isinstance(indices, Iterable):
403407
return TrivialFamily(indices)
@@ -418,7 +422,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa
418422
keys=indices)
419423
420424
421-
class AbstractFamily(Parent):
425+
cdef class AbstractFamily(Parent):
422426
"""
423427
The abstract class for family
424428
@@ -436,7 +440,6 @@ def hidden_keys(self):
436440
"""
437441
return []
438442
439-
@abstract_method
440443
def keys(self):
441444
"""
442445
Return the keys of the family.
@@ -447,8 +450,8 @@ def keys(self):
447450
sage: sorted(f.keys())
448451
[3, 4, 7]
449452
"""
453+
raise NotImplementedError
450454
451-
@abstract_method(optional=True)
452455
def values(self):
453456
"""
454457
Return the elements (values) of this family.
@@ -459,6 +462,7 @@ def values(self):
459462
sage: sorted(f.values())
460463
['aa', 'bb', 'cc']
461464
"""
465+
raise NotImplementedError
462466
463467
def items(self):
464468
"""
@@ -535,7 +539,8 @@ def inverse_family(self):
535539
return Family({self[k]: k for k in self.keys()})
536540
537541
538-
class FiniteFamily(AbstractFamily):
542+
543+
cdef class FiniteFamily(AbstractFamily):
539544
r"""
540545
A :class:`FiniteFamily` is an associative container which models a finite
541546
family `(f_i)_{i \in I}`. Its elements `f_i` are therefore its
@@ -712,8 +717,8 @@ def __eq__(self, other):
712717
False
713718
"""
714719
return (isinstance(other, self.__class__) and
715-
self._keys == other._keys and
716-
self._dictionary == other._dictionary)
720+
self._keys == (<FiniteFamily> other)._keys and
721+
self._dictionary == (<FiniteFamily> other)._dictionary)
717722
718723
def _repr_(self):
719724
"""
@@ -869,15 +874,17 @@ def __getitem__(self, i):
869874
...
870875
KeyError
871876
"""
872-
if i in self._dictionary:
873-
return self._dictionary[i]
874-
875-
if i not in self.hidden_dictionary:
876-
if i not in self._hidden_keys:
877-
raise KeyError
878-
self.hidden_dictionary[i] = self.hidden_function(i)
879-
880-
return self.hidden_dictionary[i]
877+
try:
878+
return FiniteFamily.__getitem__(self, i)
879+
except KeyError:
880+
try:
881+
return self.hidden_dictionary[i]
882+
except KeyError:
883+
if i not in self._hidden_keys:
884+
raise KeyError
885+
v = self.hidden_function(i)
886+
self.hidden_dictionary[i] = v
887+
return v
881888
882889
def hidden_keys(self):
883890
"""
@@ -902,11 +909,11 @@ def __getstate__(self):
902909
"""
903910
from sage.misc.fpickle import pickle_function
904911
f = pickle_function(self.hidden_function)
905-
return {'dictionary': self._dictionary,
906-
'hidden_keys': self._hidden_keys,
907-
'hidden_dictionary': self.hidden_dictionary,
908-
'hidden_function': f,
909-
'keys': self._keys}
912+
state = super().__getstate__()
913+
state.update({'hidden_keys': self._hidden_keys,
914+
'hidden_dictionary': self.hidden_dictionary,
915+
'hidden_function': f})
916+
return state
910917
911918
def __setstate__(self, d):
912919
"""
@@ -922,7 +929,7 @@ def __setstate__(self, d):
922929
6
923930
"""
924931
hidden_function = d['hidden_function']
925-
if isinstance(hidden_function, str):
932+
if isinstance(hidden_function, (str, bytes)):
926933
# Let's assume that hidden_function is an unpickled function.
927934
from sage.misc.fpickle import unpickle_function
928935
hidden_function = unpickle_function(hidden_function)
@@ -1074,7 +1081,7 @@ def _repr_(self):
10741081
"""
10751082
if self.function_name is not None:
10761083
name = self.function_name + "(i)"
1077-
elif isinstance(self.function, type(lambda x: 1)):
1084+
elif isinstance(self.function, types.LambdaType):
10781085
name = self.function.__name__
10791086
name = name + "(i)"
10801087
else:

0 commit comments

Comments
 (0)