Skip to content

Commit ff8e473

Browse files
authored
Merge branch 'main' into doc-reorg
2 parents 79a1134 + cca6019 commit ff8e473

File tree

11 files changed

+382
-98
lines changed

11 files changed

+382
-98
lines changed

pyomo/common/collections/component_map.py

+14
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ def _unhashable(val):
4747
def _tuple(self, val):
4848
return tuple(self[i.__class__](i) for i in val)
4949

50+
def hashable(self, obj, hashable=None):
51+
if isinstance(obj, type):
52+
cls = obj
53+
else:
54+
cls = type(obj)
55+
if hashable is None:
56+
fcn = self.get(cls, None)
57+
if fcn is None:
58+
raise KeyError(obj)
59+
return fcn is self._hashable
60+
self[cls] = self._hashable if hashable else self._unhashable
61+
5062

5163
_hasher = _Hasher()
5264

@@ -78,6 +90,8 @@ class ComponentMap(AutoSlots.Mixin, collections.abc.MutableMapping):
7890

7991
__slots__ = ("_dict",)
8092
__autoslot_mappers__ = {'_dict': _rehash_keys}
93+
# Expose a "public" interface to the global _hasher dict
94+
hasher = _hasher
8195

8296
def __init__(self, *args, **kwds):
8397
# maps id_hash(obj) -> (obj,val)

pyomo/common/collections/component_set.py

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class ComponentSet(AutoSlots.Mixin, collections_MutableSet):
6161

6262
__slots__ = ("_data",)
6363
__autoslot_mappers__ = {'_data': _rehash_keys}
64+
# Expose a "public" interface to the global _hasher dict
65+
hasher = _hasher
6466

6567
def __init__(self, iterable=None):
6668
# maps id_hash(obj) -> obj

pyomo/common/tests/test_component_map.py

+21
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@ def test_tuple(self):
4949
self.assertIn((1, (2, m.v)), m.cm)
5050
self.assertNotIn((1, (2, m.v)), i.cm)
5151

52+
def test_hasher(self):
53+
m = ComponentMap()
54+
a = 'str'
55+
m[a] = 5
56+
self.assertTrue(m.hasher.hashable(a))
57+
self.assertTrue(m.hasher.hashable(str))
58+
self.assertEqual(m._dict, {a: (a, 5)})
59+
del m[a]
60+
61+
m.hasher.hashable(a, False)
62+
m[a] = 5
63+
self.assertFalse(m.hasher.hashable(a))
64+
self.assertFalse(m.hasher.hashable(str))
65+
self.assertEqual(m._dict, {id(a): (a, 5)})
66+
67+
class TMP:
68+
pass
69+
70+
with self.assertRaises(KeyError):
71+
m.hasher.hashable(TMP)
72+
5273

5374
class TestDefaultComponentMap(unittest.TestCase):
5475
def test_default_component_map(self):

pyomo/core/base/param.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -898,8 +898,12 @@ def _pprint(self):
898898
dataGen = lambda k, v: [v._value]
899899
else:
900900
dataGen = lambda k, v: [v]
901+
if self.index_set().isfinite() or self._default_val is Param.NoValue:
902+
_len = len(self)
903+
else:
904+
_len = 'inf'
901905
headers = [
902-
("Size", len(self)),
906+
("Size", _len),
903907
("Index", self._index_set if self.is_indexed() else None),
904908
("Domain", self.domain.name),
905909
("Default", default),

pyomo/core/base/set.py

+24-14
Original file line numberDiff line numberDiff line change
@@ -1493,18 +1493,7 @@ def _cb_validate_filter(self, mode, val_iter):
14931493
try:
14941494
flag = fcn(block, (), *vstar)
14951495
if flag:
1496-
deprecation_warning(
1497-
f"{self.__class__.__name__} {self.name}: '{mode}=' "
1498-
"callback signature matched (block, *value). "
1499-
"Please update the callback to match the signature "
1500-
f"(block, value{', *index' if comp.is_indexed() else ''}).",
1501-
version='6.8.0',
1502-
)
1503-
orig_fcn = fcn._fcn
1504-
fcn = ParameterizedScalarCallInitializer(
1505-
lambda m, v: orig_fcn(m, *v), True
1506-
)
1507-
setattr(comp, '_' + mode, fcn)
1496+
self._filter_validate_scalar_api_deprecation(mode, warning=True)
15081497
yield value
15091498
continue
15101499
except TypeError:
@@ -1545,6 +1534,21 @@ def _cb_validate_filter(self, mode, val_iter):
15451534
)
15461535
raise exc from None
15471536

1537+
def _filter_validate_scalar_api_deprecation(self, mode, warning):
1538+
comp = self.parent_component()
1539+
fcn = getattr(comp, '_' + mode)
1540+
if warning:
1541+
deprecation_warning(
1542+
f"{self.__class__.__name__} {self.name}: '{mode}=' "
1543+
"callback signature matched (block, *value). "
1544+
"Please update the callback to match the signature "
1545+
f"(block, value{', *index' if comp.is_indexed() else ''}).",
1546+
version='6.8.0',
1547+
)
1548+
orig_fcn = fcn._fcn
1549+
fcn = ParameterizedScalarCallInitializer(lambda m, v: orig_fcn(m, *v), True)
1550+
setattr(comp, '_' + mode, fcn)
1551+
15481552
def _cb_normalized_dimen_verifier(self, dimen, val_iter):
15491553
for value in val_iter:
15501554
if value.__class__ in native_types:
@@ -2265,14 +2269,20 @@ def __init__(self, *args, **kwds):
22652269
self._init_values._init = CountedCallInitializer(
22662270
self, self._init_values._init
22672271
)
2268-
# HACK: the DAT parser needs to know the domain of a set in
2269-
# order to correctly parse the data stream.
2272+
22702273
if not self.is_indexed():
2274+
# HACK: the DAT parser needs to know the domain of a set in
2275+
# order to correctly parse the data stream.
22712276
if self._init_domain.constant():
22722277
self._domain = self._init_domain(self.parent_block(), None, self)
22732278
if self._init_dimen.constant():
22742279
self._dimen = self._init_dimen(self.parent_block(), None)
22752280

2281+
if self._filter.__class__ is ParameterizedIndexedCallInitializer:
2282+
self._filter_validate_scalar_api_deprecation('filter', warning=False)
2283+
if self._validate.__class__ is ParameterizedIndexedCallInitializer:
2284+
self._filter_validate_scalar_api_deprecation('validate', warning=False)
2285+
22762286
@deprecated(
22772287
"check_values() is deprecated: Sets only contain valid members", version='5.7'
22782288
)

pyomo/core/tests/unit/test_param.py

+64
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,70 @@ def test_scalar_set_mutable_when_not_present(self):
15611561
m.p = 20
15621562
self.assertEqual(m.x_p.bounds, (0, 20))
15631563

1564+
def test_nonfinite_pprint(self):
1565+
m = ConcreteModel()
1566+
1567+
# Test from #3379
1568+
m.p = Param(Any, default=1)
1569+
self.assertEqual(m.p['foo'], 1)
1570+
OUT = StringIO()
1571+
m.p.pprint(OUT)
1572+
self.assertEqual(
1573+
OUT.getvalue(),
1574+
"p : Size=inf, Index=Any, Domain=Any, Default=1, Mutable=False\n"
1575+
" Key : Value\n",
1576+
)
1577+
1578+
# Other useful checks
1579+
m.q = Param(Any, default=1, initialize={1: 2, 'a': 3, 'bb': 4})
1580+
OUT = StringIO()
1581+
m.q.pprint(OUT)
1582+
self.assertEqual(
1583+
OUT.getvalue(),
1584+
"q : Size=inf, Index=Any, Domain=Any, Default=1, Mutable=False\n"
1585+
" Key : Value\n"
1586+
" 1 : 2\n"
1587+
" a : 3\n"
1588+
" bb : 4\n",
1589+
)
1590+
1591+
m.r = Param(Any, initialize={1: 2, 'a': 3, 'bb': 4})
1592+
OUT = StringIO()
1593+
m.r.pprint(OUT)
1594+
self.assertEqual(
1595+
OUT.getvalue(),
1596+
"r : Size=3, Index=Any, Domain=Any, Default=None, Mutable=False\n"
1597+
" Key : Value\n"
1598+
" 1 : 2\n"
1599+
" a : 3\n"
1600+
" bb : 4\n",
1601+
)
1602+
1603+
# Other useful (mutable) checks
1604+
m.q = Param(Any, default=1, mutable=True, initialize={1: 2, 'a': 3, 'bb': 4})
1605+
OUT = StringIO()
1606+
m.q.pprint(OUT)
1607+
self.assertEqual(
1608+
OUT.getvalue(),
1609+
"q : Size=inf, Index=Any, Domain=Any, Default=1, Mutable=True\n"
1610+
" Key : Value\n"
1611+
" 1 : 2\n"
1612+
" a : 3\n"
1613+
" bb : 4\n",
1614+
)
1615+
1616+
m.r = Param(Any, mutable=True, initialize={1: 2, 'a': 3, 'bb': 4})
1617+
OUT = StringIO()
1618+
m.r.pprint(OUT)
1619+
self.assertEqual(
1620+
OUT.getvalue(),
1621+
"r : Size=3, Index=Any, Domain=Any, Default=None, Mutable=True\n"
1622+
" Key : Value\n"
1623+
" 1 : 2\n"
1624+
" a : 3\n"
1625+
" bb : 4\n",
1626+
)
1627+
15641628

15651629
def createNonIndexedParamMethod(func, init_xy, new_xy, tol=1e-10):
15661630
def testMethod(self):

pyomo/core/tests/unit/test_set.py

+36-8
Original file line numberDiff line numberDiff line change
@@ -4181,6 +4181,19 @@ def test_indexed_set(self):
41814181
self.assertIs(type(m.I[3]), InsertionOrderSetData)
41824182
self.assertEqual(m.I.data(), {1: (4, 2, 5), 2: (4, 2, 5), 3: (4, 2, 5)})
41834183

4184+
# Explicit (constant dict) construction
4185+
m = ConcreteModel()
4186+
m.I = Set([1, 2], initialize={1: (4, 2, 5), 2: (7, 6)})
4187+
self.assertEqual(len(m.I), 2)
4188+
self.assertEqual(list(m.I[1]), [4, 2, 5])
4189+
self.assertEqual(list(m.I[2]), [7, 6])
4190+
self.assertIsNot(m.I[1], m.I[2])
4191+
self.assertTrue(m.I[1].isordered())
4192+
self.assertTrue(m.I[2].isordered())
4193+
self.assertIs(type(m.I[1]), InsertionOrderSetData)
4194+
self.assertIs(type(m.I[2]), InsertionOrderSetData)
4195+
self.assertEqual(m.I.data(), {1: (4, 2, 5), 2: (7, 6)})
4196+
41844197
# Explicit (constant) construction
41854198
m = ConcreteModel()
41864199
m.I = Set([1, 2, 3], initialize=(4, 2, 5), ordered=Set.SortedOrder)
@@ -4255,7 +4268,7 @@ def test_indexing(self):
42554268
def test_add_filter_validate(self):
42564269
m = ConcreteModel()
42574270
m.I = Set(domain=Integers)
4258-
self.assertIs(m.I.filter, None)
4271+
self.assertIs(m.I._filter, None)
42594272
with self.assertRaisesRegex(
42604273
ValueError,
42614274
r"Cannot add value 1.5 to Set I.\n"
@@ -4302,7 +4315,7 @@ def _l_tri(model, i, j):
43024315
return i >= j
43034316

43044317
m.K = Set(initialize=RangeSet(3) * RangeSet(3), filter=_l_tri)
4305-
self.assertIsInstance(m.K.filter, ParameterizedScalarCallInitializer)
4318+
self.assertIsInstance(m.K._filter, ParameterizedScalarCallInitializer)
43064319
self.assertEqual(list(m.K), [(1, 1), (2, 1), (2, 2), (3, 1), (3, 2), (3, 3)])
43074320

43084321
output = StringIO()
@@ -4334,6 +4347,18 @@ def _lt_3(model, i):
43344347
self.assertEqual(output.getvalue(), "")
43354348
self.assertEqual(list(m.L[2]), [1, 2, 0])
43364349

4350+
# This tests that the deprecation path works correctly in the
4351+
# case that the callback doesn't raise an error or ever return
4352+
# False
4353+
4354+
def _l_off_diag(model, i, j):
4355+
self.assertIs(model, m)
4356+
return i != j
4357+
4358+
m.M = Set(initialize=RangeSet(3) * RangeSet(3), filter=_l_off_diag)
4359+
self.assertIsInstance(m.M._filter, ParameterizedScalarCallInitializer)
4360+
self.assertEqual(list(m.M), [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)])
4361+
43374362
m = ConcreteModel()
43384363

43394364
def _validate(model, val):
@@ -4374,12 +4399,15 @@ def _validate(model, i, j):
43744399
m.I2 = Set(validate=_validate)
43754400
with LoggingIntercept(module='pyomo.core') as output:
43764401
self.assertTrue(m.I2.add((0, 1)))
4377-
self.assertRegex(
4378-
output.getvalue().replace('\n', ' '),
4379-
r"DEPRECATED: OrderedScalarSet I2: 'validate=' callback "
4380-
r"signature matched \(block, \*value\). Please update the "
4381-
r"callback to match the signature \(block, value\)",
4382-
)
4402+
# Note that we are not emitting a deprecation warning (yet)
4403+
# for scalar sets
4404+
# self.assertEqual(output.getvalue(), "")
4405+
# output.getvalue().replace('\n', ' '),
4406+
# r"DEPRECATED: OrderedScalarSet I2: 'validate=' callback "
4407+
# r"signature matched \(block, \*value\). Please update the "
4408+
# r"callback to match the signature \(block, value\)",
4409+
# )
4410+
self.assertEqual(output.getvalue(), "")
43834411
with LoggingIntercept(module='pyomo.core') as output:
43844412
with self.assertRaisesRegex(
43854413
ValueError,

0 commit comments

Comments
 (0)