Skip to content

Commit e0fcc81

Browse files
authored
Merge pull request Pyomo#3804 from jsiirola/tabular-writer-nosorting
Remove sorting from `tabular_writer`
2 parents e4c9aa8 + 6280966 commit e0fcc81

File tree

25 files changed

+534
-263
lines changed

25 files changed

+534
-263
lines changed

doc/OnlineDocs/src/kernel/examples.txt

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -76,29 +76,22 @@
7676
3 : -5.0 : vl[3] - v : 5.0 : True
7777

7878
3 SOSConstraint Declarations
79-
sd : Size=2 Index= OrderedScalarSet
80-
1
81-
Type=1
82-
Weight : Variable
83-
1 : vd[1]
84-
2 : vd[2]
85-
2
86-
Type=1
87-
Weight : Variable
88-
1 : vl[1]
89-
2 : vl[2]
90-
3 : vl[3]
91-
sos1 : Size=1
92-
Type=1
93-
Weight : Variable
94-
1 : vl[1]
95-
2 : vl[2]
96-
3 : vl[3]
97-
sos2 : Size=1
98-
Type=2
99-
Weight : Variable
100-
1 : vd[1]
101-
2 : vd[2]
79+
sd : Size=2, Index={1, 2}
80+
Key : Type : Weight : Variable
81+
1 : 1 : 1 : vd[1]
82+
: : 2 : vd[2]
83+
2 : 1 : 1 : vl[1]
84+
: : 2 : vl[2]
85+
: : 3 : vl[3]
86+
sos1 : Size=1, Index=None
87+
Key : Type : Weight : Variable
88+
None : 1 : 1 : vl[1]
89+
: : 2 : vl[2]
90+
: : 3 : vl[3]
91+
sos2 : Size=1, Index=None
92+
Key : Type : Weight : Variable
93+
None : 2 : 1 : vd[1]
94+
: : 2 : vd[2]
10295

10396
2 Block Declarations
10497
b : Size=1, Index=None, Active=True
@@ -120,13 +113,12 @@
120113
3 : 1.0 : pw.SOS2_y[0] + pw.SOS2_y[1] + pw.SOS2_y[2] + pw.SOS2_y[3] : 1.0 : True
121114

122115
1 SOSConstraint Declarations
123-
SOS2_sosconstraint : Size=1
124-
Type=2
125-
Weight : Variable
126-
1 : pw.SOS2_y[0]
127-
2 : pw.SOS2_y[1]
128-
3 : pw.SOS2_y[2]
129-
4 : pw.SOS2_y[3]
116+
SOS2_sosconstraint : Size=1, Index=None
117+
Key : Type : Weight : Variable
118+
None : 2 : 1 : pw.SOS2_y[0]
119+
: : 2 : pw.SOS2_y[1]
120+
: : 3 : pw.SOS2_y[2]
121+
: : 4 : pw.SOS2_y[3]
130122

131123
3 Declarations: SOS2_y SOS2_constraint SOS2_sosconstraint
132124

pyomo/common/formatting.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,11 @@ def tabular_writer(ostream, prefix, data, header, row_generator):
154154
_rows[_key] = None
155155
continue
156156

157+
# Include the key for only the first line in a rowset, and only
158+
# if we printed out a header (if there is no header, then the
159+
# key is not included)
157160
_rows[_key] = [
158-
((tostr("" if i else _key),) if header else ())
161+
(("" if i else tostr(_key),) if header else ())
159162
+ tuple(tostr(x) for x in _r)
160163
for i, _r in enumerate(_rowSet)
161164
]
@@ -190,7 +193,7 @@ def tabular_writer(ostream, prefix, data, header, row_generator):
190193

191194
if any(' ' in r[-1] for x in _rows.values() if x is not None for r in x):
192195
_width[-1] = '%s'
193-
for _key in sorted_robust(_rows):
196+
for _key in _rows:
194197
_rowSet = _rows[_key]
195198
if not _rowSet:
196199
_rowSet = [[_key] + [None] * (len(_width) - 1)]

pyomo/common/tests/test_formatting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ def test_no_header(self):
9696
data = {(2,): (["a", 1], 1), (1, 3): ({1: 'a', 2: '2'}, '2')}
9797
tabular_writer(os, "", data.items(), [], lambda k, v: v)
9898
ref = u"""
99-
{1: 'a', 2: '2'} : 2
10099
['a', 1] : 1
100+
{1: 'a', 2: '2'} : 2
101101
"""
102102
self.assertEqual(ref.strip(), os.getvalue().strip())
103103

pyomo/contrib/cp/sequence_var.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def _pprint(self):
134134
]
135135
return (
136136
headers,
137-
self._data.items(),
137+
self.items,
138138
("IntervalVars",),
139139
lambda k, v: ['[' + ', '.join(iv.name for iv in v.interval_vars) + ']'],
140140
)

pyomo/core/base/block.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,7 @@ def is_constructed(self):
18361836
return False
18371837
return True
18381838

1839-
def _pprint_blockdata_components(self, ostream):
1839+
def _pprint_blockdata_components(self, ostream, sort):
18401840
#
18411841
# We hard-code the order of the core Pyomo modeling
18421842
# components, to ensure that the output follows the logical order
@@ -1862,15 +1862,18 @@ def _pprint_blockdata_components(self, ostream):
18621862

18631863
indented_ostream = StreamIndenter(ostream, self._PPRINT_INDENT)
18641864
for item in items:
1865-
keys = sorted(self.component_map(item))
1865+
if SortComponents.ALPHABETICAL in sort:
1866+
keys = sorted(self.component_map(item))
1867+
else:
1868+
keys = list(self.component_map(item))
18661869
if not keys:
18671870
continue
18681871
#
18691872
# NOTE: these conditional checks should not be hard-coded.
18701873
#
18711874
ostream.write("%d %s Declarations\n" % (len(keys), item.__name__))
18721875
for key in keys:
1873-
self.component(key).pprint(ostream=indented_ostream)
1876+
self.component(key).pprint(ostream=indented_ostream, sort=sort)
18741877
ostream.write("\n")
18751878
#
18761879
# Model Order
@@ -2237,25 +2240,26 @@ def construct(self, data=None):
22372240
_BlockConstruction.data.pop(id(self), None)
22382241
timer.report()
22392242

2240-
def _pprint_callback(self, ostream, idx, data):
2243+
def _pprint_callback(self, ostream, sort, idx, data):
22412244
if not self.is_indexed():
2242-
data._pprint_blockdata_components(ostream)
2245+
data._pprint_blockdata_components(ostream, sort)
22432246
else:
22442247
ostream.write("%s : Active=%s\n" % (data.name, data.active))
22452248
ostream = StreamIndenter(ostream, self._PPRINT_INDENT)
2246-
data._pprint_blockdata_components(ostream)
2249+
data._pprint_blockdata_components(ostream, sort)
22472250

22482251
def _pprint(self):
2249-
_attrs = [
2250-
("Size", len(self)),
2251-
("Index", self._index_set if self.is_indexed() else None),
2252-
('Active', self.active),
2253-
]
22542252
# HACK: suppress the top-level block header (for historical reasons)
22552253
if self.parent_block() is None and not self.is_indexed():
2256-
return None, self._data.items(), None, self._pprint_callback
2254+
_attrs = None
22572255
else:
2258-
return _attrs, self._data.items(), None, self._pprint_callback
2256+
_attrs = [
2257+
("Size", len(self)),
2258+
("Index", self._index_set if self.is_indexed() else None),
2259+
('Active', self.active),
2260+
]
2261+
2262+
return _attrs, self.items, None, self._pprint_callback
22592263

22602264
def display(self, filename=None, ostream=None, prefix=""):
22612265
"""

pyomo/core/base/boolean_var.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ def _pprint(self):
456456
("Size", len(self)),
457457
("Index", self._index_set if self.is_indexed() else None),
458458
],
459-
self._data.items(),
459+
self.items,
460460
("Value", "Fixed", "Stale"),
461461
lambda k, v: [v.value, v.fixed, v.stale],
462462
)

pyomo/core/base/component.py

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from pyomo.common.sorting import sorted_robust
3232
from pyomo.core.pyomoobject import PyomoObject
3333
from pyomo.core.base.component_namer import name_repr, index_repr
34+
from pyomo.core.base.enums import SortComponents
3435
from pyomo.core.base.global_set import UnindexedComponent_index
3536
from pyomo.core.base.initializer import PartialInitializer
3637

@@ -41,6 +42,7 @@
4142
)
4243

4344
_ref_types = {type(None), weakref_ref}
45+
DEFAULT_PPRINT_SORT = SortComponents.ALPHABETICAL | SortComponents.SORTED_INDICES
4446

4547

4648
class ModelComponentFactoryClass(Factory):
@@ -274,7 +276,7 @@ def __deepcopy_field__(self, value, memo, slot_name):
274276
def cname(self, *args, **kwds):
275277
return self.getname(*args, **kwds)
276278

277-
def pprint(self, ostream=None, verbose=False, prefix=""):
279+
def pprint(self, ostream=None, verbose=False, prefix="", sort=NOTSET):
278280
"""Print component information
279281
280282
Note that this method is generally only reachable through
@@ -292,12 +294,13 @@ def pprint(self, ostream=None, verbose=False, prefix=""):
292294
_name = comp.local_name
293295
else:
294296
# restrict output to only this data object
295-
_data = iter(((self.index(), self),))
297+
_data = lambda _sort: iter(((self.index(), self),))
296298
_name = "{Member of %s}" % (comp.local_name,)
297299
self._pprint_base_impl(
298300
ostream,
299301
verbose,
300302
prefix,
303+
sort,
301304
_name,
302305
comp.doc,
303306
comp.is_constructed(),
@@ -348,6 +351,7 @@ def _pprint_base_impl(
348351
ostream,
349352
verbose,
350353
prefix,
354+
sort,
351355
_name,
352356
_doc,
353357
_constructed,
@@ -361,6 +365,10 @@ def _pprint_base_impl(
361365
if prefix:
362366
ostream = StreamIndenter(ostream, prefix)
363367

368+
if sort is NOTSET:
369+
sort = DEFAULT_PPRINT_SORT
370+
sort = SortComponents(sort)
371+
364372
# FIXME: HACK for backwards compatibility with suppressing the
365373
# header for the top block
366374
if not _attr and self.parent_block() is None:
@@ -391,23 +399,37 @@ def _pprint_base_impl(
391399
return
392400

393401
if type(_fcn) is tuple:
402+
# Exception to the standard formatter case: with two
403+
# callbacks, we will use the first to generate the normal
404+
# table, then call the second callback for each data.
405+
# Currently only used by Complimentarity (which should be
406+
# refactored to remove the need for this edge case)
394407
_fcn, _fcn2 = _fcn
395408
else:
396409
_fcn2 = None
397410

411+
if hasattr(_data, '__call__'):
412+
_data = _data(sort)
413+
398414
if _header is not None:
415+
# This is a standard component, where all the component
416+
# information is printed in a single table
399417
if _fcn2 is not None:
400-
_data_dict = dict(_data)
401-
_data = _data_dict.items()
418+
_data = list(_data)
402419
tabular_writer(ostream, '', _data, _header, _fcn)
403420
if _fcn2 is not None:
404-
for _key in sorted_robust(_data_dict):
405-
_fcn2(ostream, _key, _data_dict[_key])
421+
for _key, _val in _data:
422+
_fcn2(ostream, sort, _key, _val)
406423
elif _fcn is not None:
407-
_data_dict = dict(_data)
408-
for _key in sorted_robust(_data_dict):
409-
_fcn(ostream, _key, _data_dict[_key])
424+
# This is a non-standard component where we will not
425+
# generate a table at all, and instead defer all formatting
426+
# / printing to the callback. This is primarily used by
427+
# Blocks (and block-like things)
428+
for _key, _val in _data:
429+
_fcn(ostream, sort, _key, _val)
410430
elif _data is not None:
431+
# Catch all for everything else: assume that _pprint()
432+
# returned a formatted string.
411433
ostream.write(_data)
412434

413435

@@ -512,12 +534,13 @@ def valid_model_component(self):
512534
"""Return True if this can be used as a model component."""
513535
return True
514536

515-
def pprint(self, ostream=None, verbose=False, prefix=""):
537+
def pprint(self, ostream=None, verbose=False, prefix="", sort=NOTSET):
516538
"""Print component information"""
517539
self._pprint_base_impl(
518540
ostream,
519541
verbose,
520542
prefix,
543+
sort,
521544
self.local_name,
522545
self.doc,
523546
self.is_constructed(),

pyomo/core/base/connector.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def _initialize_members(self, initSet):
202202
for key, val in items.items():
203203
tmp.add(val, key)
204204

205-
def _pprint(self, ostream=None, verbose=False):
205+
def _pprint(self):
206206
"""Print component information."""
207207

208208
def _line_generator(k, v):
@@ -222,7 +222,7 @@ def _line_generator(k, v):
222222
("Size", len(self)),
223223
("Index", self._index_set if self.is_indexed() else None),
224224
],
225-
self._data.items(),
225+
self.items,
226226
("Name", "Size", "Variable"),
227227
_line_generator,
228228
)

pyomo/core/base/constraint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ def _pprint(self):
765765
("Index", self._index_set if self.is_indexed() else None),
766766
("Active", self.active),
767767
],
768-
self.items(),
768+
self.items,
769769
("Lower", "Body", "Upper", "Active"),
770770
lambda k, v: [
771771
"-Inf" if v.lower is None else v.lower,

pyomo/core/base/expression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ def _pprint(self):
303303
('Size', len(self)),
304304
('Index', None if (not self.is_indexed()) else self._index_set),
305305
],
306-
self.items(),
306+
self.items,
307307
("Expression",),
308308
lambda k, v: ["Undefined" if v.expr is None else v.expr],
309309
)

0 commit comments

Comments
 (0)