Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sage.sets.family: Cythonize; change MIPVariable to a subclass of FiniteFamily #35121

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
Loading