Skip to content

Commit

Permalink
sagemathgh-35121: sage.sets.family: Cythonize; change MIPVariable
Browse files Browse the repository at this point in the history
… 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 sagemath#1234" use "Introduce new method to
calculate 1+1"
-->
### 📚 Description
Fixes sagemath#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 sagemath#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: sagemath#35121
Reported by: Matthias Köppe
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Dec 17, 2023
2 parents e2e0f8d + 889f36a commit 2017233
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 61 deletions.
8 changes: 4 additions & 4 deletions src/sage/combinat/root_system/type_relabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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?
Expand Down
7 changes: 4 additions & 3 deletions src/sage/numerical/mip.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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
Expand Down
35 changes: 17 additions & 18 deletions src/sage/numerical/mip.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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``.
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -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::
Expand All @@ -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"""
Expand All @@ -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"""
Expand Down
11 changes: 11 additions & 0 deletions src/sage/sets/family.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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):
cdef public dict _dictionary
cdef public object _keys
79 changes: 43 additions & 36 deletions src/sage/sets/family.py → src/sage/sets/family.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@
Category of finite enumerated sets
"""

# ****************************************************************************
# Copyright (C) 2008 Nicolas Thiery <nthiery at users.sf.net>,
# Mike Hansen <[email protected]>,
# Florent Hivert <[email protected]>
# *****************************************************************************
# Copyright (C) 2008-2017 Nicolas Thiery <nthiery at users.sf.net>
# 2008-2009 Mike Hansen <[email protected]>
# 2008-2010 Florent Hivert <[email protected]>
# 2013-2021 Travis Scrimshaw
# 2014 Nathann Cohen
# 2017 Erik M. Bray
# 2018 Frédéric Chapoton
# 2019 Markus Wageringel
# 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
Expand All @@ -38,19 +44,18 @@
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.lazy_import import lazy_import
from sage.rings.integer import Integer
from sage.misc.cachefunc import cached_method
from sage.misc.call import AttrCallObject
from sage.sets.non_negative_integers import NonNegativeIntegers
from sage.misc.lazy_import import LazyImport
from sage.rings.infinity import Infinity
lazy_import('sage.combinat.combinat', 'CombinatorialClass')
from sage.rings.integer import Integer
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
from sage.sets.non_negative_integers import NonNegativeIntegers

CombinatorialClass = LazyImport('sage.combinat.combinat', 'CombinatorialClass')


def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=False, name=None):
Expand Down Expand Up @@ -396,8 +401,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)
Expand All @@ -418,7 +422,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
Expand All @@ -436,7 +440,6 @@ def hidden_keys(self):
"""
return []
@abstract_method
def keys(self):
"""
Return the keys of the family.
Expand All @@ -447,8 +450,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.
Expand All @@ -459,6 +462,7 @@ def values(self):
sage: sorted(f.values())
['aa', 'bb', 'cc']
"""
raise NotImplementedError
def items(self):
"""
Expand Down Expand Up @@ -535,7 +539,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
Expand Down Expand Up @@ -712,8 +717,8 @@ def __eq__(self, other):
False
"""
return (isinstance(other, self.__class__) and
self._keys == other._keys and
self._dictionary == other._dictionary)
self._keys == (<FiniteFamily> other)._keys and
self._dictionary == (<FiniteFamily> other)._dictionary)
def _repr_(self):
"""
Expand Down Expand Up @@ -869,15 +874,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 FiniteFamily.__getitem__(self, 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):
"""
Expand All @@ -902,11 +909,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):
"""
Expand All @@ -922,7 +929,7 @@ def __setstate__(self, d):
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)
Expand Down Expand Up @@ -1074,7 +1081,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:
Expand Down

0 comments on commit 2017233

Please sign in to comment.