Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f1a9739
Move name_repr, index_repr into common.formatting
jsiirola Dec 3, 2025
4bdb3e7
Minor simplification to tabular_writer (slight performance improvement)
jsiirola Dec 3, 2025
ad6e6ea
Convert Param.sparse_{keys,values,items} to return generators
jsiirola Dec 5, 2025
c249ad3
Deprecate Param.sparse_iter*
jsiirola Dec 5, 2025
7aa6e40
Simplify implementation of Param.extract_values
jsiirola Dec 5, 2025
25ec907
Update Param testing
jsiirola Dec 5, 2025
fd0ea00
Improve expected efficiency of IndexedCOmponent.keys when sorting spa…
jsiirola Dec 5, 2025
8564bfd
Minor tabular_writer clarification / efficiency fix
jsiirola Dec 5, 2025
f6f970c
Remove sorting from tabular_writer
jsiirola Dec 5, 2025
1d606b5
Remove sorting from tabular_writer
jsiirola Dec 5, 2025
9d9e0a8
Update tests to reflect the new default ordering in pprint()
jsiirola Dec 9, 2025
00a6cf1
Fix extract_values for scalar params
jsiirola Dec 10, 2025
71fc882
Expand testing for immutable scalar params
jsiirola Dec 10, 2025
01b4fab
Merge remote-tracking branch 'refs/remotes/me/tabular-writer' into ta…
jsiirola Dec 10, 2025
2a40d33
Merge branch 'main' into tabular-writer
jsiirola Dec 10, 2025
9f99c3f
Resolve bugs with keys/values/items for scalar Set objects
jsiirola Dec 14, 2025
ce0a6c1
Add the option to control sorting in pprint()
jsiirola Dec 15, 2025
7f8687c
Make IndexedCOmponent .items() and .values() more robust
jsiirola Dec 15, 2025
31a05bc
NFC: fix comment
jsiirola Dec 15, 2025
cb77a4f
Revert "Move name_repr, index_repr into common.formatting"
jsiirola Dec 15, 2025
57ef452
Revert "Minor simplification to tabular_writer (slight performance im…
jsiirola Dec 15, 2025
f251335
Update SOSConstraint to use standard _pprint() infrastructure
jsiirola Dec 22, 2025
e3d6e82
Remove unneeded Suffix.pprint() overload
jsiirola Dec 22, 2025
35d2076
Merge branch 'main' into tabular-writer-nosorting
jsiirola Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 22 additions & 30 deletions doc/OnlineDocs/src/kernel/examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,29 +76,22 @@
3 : -5.0 : vl[3] - v : 5.0 : True

3 SOSConstraint Declarations
sd : Size=2 Index= OrderedScalarSet
1
Type=1
Weight : Variable
1 : vd[1]
2 : vd[2]
2
Type=1
Weight : Variable
1 : vl[1]
2 : vl[2]
3 : vl[3]
sos1 : Size=1
Type=1
Weight : Variable
1 : vl[1]
2 : vl[2]
3 : vl[3]
sos2 : Size=1
Type=2
Weight : Variable
1 : vd[1]
2 : vd[2]
sd : Size=2, Index={1, 2}
Key : Type : Weight : Variable
1 : 1 : 1 : vd[1]
: : 2 : vd[2]
2 : 1 : 1 : vl[1]
: : 2 : vl[2]
: : 3 : vl[3]
sos1 : Size=1, Index=None
Key : Type : Weight : Variable
None : 1 : 1 : vl[1]
: : 2 : vl[2]
: : 3 : vl[3]
sos2 : Size=1, Index=None
Key : Type : Weight : Variable
None : 2 : 1 : vd[1]
: : 2 : vd[2]

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

1 SOSConstraint Declarations
SOS2_sosconstraint : Size=1
Type=2
Weight : Variable
1 : pw.SOS2_y[0]
2 : pw.SOS2_y[1]
3 : pw.SOS2_y[2]
4 : pw.SOS2_y[3]
SOS2_sosconstraint : Size=1, Index=None
Key : Type : Weight : Variable
None : 2 : 1 : pw.SOS2_y[0]
: : 2 : pw.SOS2_y[1]
: : 3 : pw.SOS2_y[2]
: : 4 : pw.SOS2_y[3]

3 Declarations: SOS2_y SOS2_constraint SOS2_sosconstraint

Expand Down
7 changes: 5 additions & 2 deletions pyomo/common/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,11 @@ def tabular_writer(ostream, prefix, data, header, row_generator):
_rows[_key] = None
continue

# Include the key for only the first line in a rowset, and only
# if we printed out a header (if there is no header, then then
# key is not included)
_rows[_key] = [
((tostr("" if i else _key),) if header else ())
(("" if i else tostr(_key),) if header else ())
+ tuple(tostr(x) for x in _r)
for i, _r in enumerate(_rowSet)
]
Expand Down Expand Up @@ -186,7 +189,7 @@ def tabular_writer(ostream, prefix, data, header, row_generator):

if any(' ' in r[-1] for x in _rows.values() if x is not None for r in x):
_width[-1] = '%s'
for _key in sorted_robust(_rows):
for _key in _rows:
_rowSet = _rows[_key]
if not _rowSet:
_rowSet = [[_key] + [None] * (len(_width) - 1)]
Expand Down
2 changes: 1 addition & 1 deletion pyomo/common/tests/test_formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ def test_no_header(self):
data = {(2,): (["a", 1], 1), (1, 3): ({1: 'a', 2: '2'}, '2')}
tabular_writer(os, "", data.items(), [], lambda k, v: v)
ref = u"""
{1: 'a', 2: '2'} : 2
['a', 1] : 1
{1: 'a', 2: '2'} : 2
"""
self.assertEqual(ref.strip(), os.getvalue().strip())

Expand Down
2 changes: 1 addition & 1 deletion pyomo/contrib/cp/sequence_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def _pprint(self):
]
return (
headers,
self._data.items(),
self.items,
("IntervalVars",),
lambda k, v: ['[' + ', '.join(iv.name for iv in v.interval_vars) + ']'],
)
Expand Down
30 changes: 17 additions & 13 deletions pyomo/core/base/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -1851,7 +1851,7 @@ def is_constructed(self):
return False
return True

def _pprint_blockdata_components(self, ostream):
def _pprint_blockdata_components(self, ostream, sort):
#
# We hard-code the order of the core Pyomo modeling
# components, to ensure that the output follows the logical order
Expand All @@ -1877,15 +1877,18 @@ def _pprint_blockdata_components(self, ostream):

indented_ostream = StreamIndenter(ostream, self._PPRINT_INDENT)
for item in items:
keys = sorted(self.component_map(item))
if SortComponents.ALPHABETICAL in sort:
keys = sorted(self.component_map(item))
else:
keys = list(self.component_map(item))
if not keys:
continue
#
# NOTE: these conditional checks should not be hard-coded.
#
ostream.write("%d %s Declarations\n" % (len(keys), item.__name__))
for key in keys:
self.component(key).pprint(ostream=indented_ostream)
self.component(key).pprint(ostream=indented_ostream, sort=sort)
ostream.write("\n")
#
# Model Order
Expand Down Expand Up @@ -2252,25 +2255,26 @@ def construct(self, data=None):
_BlockConstruction.data.pop(id(self), None)
timer.report()

def _pprint_callback(self, ostream, idx, data):
def _pprint_callback(self, ostream, sort, idx, data):
if not self.is_indexed():
data._pprint_blockdata_components(ostream)
data._pprint_blockdata_components(ostream, sort)
else:
ostream.write("%s : Active=%s\n" % (data.name, data.active))
ostream = StreamIndenter(ostream, self._PPRINT_INDENT)
data._pprint_blockdata_components(ostream)
data._pprint_blockdata_components(ostream, sort)

def _pprint(self):
_attrs = [
("Size", len(self)),
("Index", self._index_set if self.is_indexed() else None),
('Active', self.active),
]
# HACK: suppress the top-level block header (for historical reasons)
if self.parent_block() is None and not self.is_indexed():
return None, self._data.items(), None, self._pprint_callback
_attrs = None
else:
return _attrs, self._data.items(), None, self._pprint_callback
_attrs = [
("Size", len(self)),
("Index", self._index_set if self.is_indexed() else None),
('Active', self.active),
]

return _attrs, self.items, None, self._pprint_callback

def display(self, filename=None, ostream=None, prefix=""):
"""
Expand Down
2 changes: 1 addition & 1 deletion pyomo/core/base/boolean_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ def _pprint(self):
("Size", len(self)),
("Index", self._index_set if self.is_indexed() else None),
],
self._data.items(),
self.items,
("Value", "Fixed", "Stale"),
lambda k, v: [v.value, v.fixed, v.stale],
)
Expand Down
43 changes: 33 additions & 10 deletions pyomo/core/base/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from pyomo.common.sorting import sorted_robust
from pyomo.core.pyomoobject import PyomoObject
from pyomo.core.base.component_namer import name_repr, index_repr
from pyomo.core.base.enums import SortComponents
from pyomo.core.base.global_set import UnindexedComponent_index
from pyomo.core.base.initializer import PartialInitializer

Expand All @@ -41,6 +42,7 @@
)

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


class ModelComponentFactoryClass(Factory):
Expand Down Expand Up @@ -274,7 +276,7 @@ def __deepcopy_field__(self, value, memo, slot_name):
def cname(self, *args, **kwds):
return self.getname(*args, **kwds)

def pprint(self, ostream=None, verbose=False, prefix=""):
def pprint(self, ostream=None, verbose=False, prefix="", sort=NOTSET):
"""Print component information

Note that this method is generally only reachable through
Expand All @@ -292,12 +294,13 @@ def pprint(self, ostream=None, verbose=False, prefix=""):
_name = comp.local_name
else:
# restrict output to only this data object
_data = iter(((self.index(), self),))
_data = lambda _sort: iter(((self.index(), self),))
_name = "{Member of %s}" % (comp.local_name,)
self._pprint_base_impl(
ostream,
verbose,
prefix,
sort,
_name,
comp.doc,
comp.is_constructed(),
Expand Down Expand Up @@ -348,6 +351,7 @@ def _pprint_base_impl(
ostream,
verbose,
prefix,
sort,
_name,
_doc,
_constructed,
Expand All @@ -361,6 +365,10 @@ def _pprint_base_impl(
if prefix:
ostream = StreamIndenter(ostream, prefix)

if sort is NOTSET:
sort = DEFAULT_PPRINT_SORT
sort = SortComponents(sort)

# FIXME: HACK for backwards compatibility with suppressing the
# header for the top block
if not _attr and self.parent_block() is None:
Expand Down Expand Up @@ -395,23 +403,37 @@ def _pprint_base_impl(
return

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

if hasattr(_data, '__call__'):
_data = _data(sort)

if _header is not None:
# This is a standard component, where all the component
# information is printed in a single table
if _fcn2 is not None:
_data_dict = dict(_data)
_data = _data_dict.items()
_data = list(_data)
tabular_writer(ostream, '', _data, _header, _fcn)
if _fcn2 is not None:
for _key in sorted_robust(_data_dict):
_fcn2(ostream, _key, _data_dict[_key])
for _key, _val in _data:
_fcn2(ostream, sort, _key, _val)
elif _fcn is not None:
_data_dict = dict(_data)
for _key in sorted_robust(_data_dict):
_fcn(ostream, _key, _data_dict[_key])
# This is a non-standard component where we will not
# generate a table at all, and instead defer all formatting
# / printing to the callback. This is primarily used by
# BLocks (and block-like things)
for _key, _val in _data:
_fcn(ostream, sort, _key, _val)
elif _data is not None:
# Catch all for everything else: assume that _pprint()
# returned a formatted string.
ostream.write(_data)


Expand Down Expand Up @@ -516,12 +538,13 @@ def valid_model_component(self):
"""Return True if this can be used as a model component."""
return True

def pprint(self, ostream=None, verbose=False, prefix=""):
def pprint(self, ostream=None, verbose=False, prefix="", sort=NOTSET):
"""Print component information"""
self._pprint_base_impl(
ostream,
verbose,
prefix,
sort,
self.local_name,
self.doc,
self.is_constructed(),
Expand Down
4 changes: 2 additions & 2 deletions pyomo/core/base/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def _initialize_members(self, initSet):
for key, val in items.items():
tmp.add(val, key)

def _pprint(self, ostream=None, verbose=False):
def _pprint(self):
"""Print component information."""

def _line_generator(k, v):
Expand All @@ -222,7 +222,7 @@ def _line_generator(k, v):
("Size", len(self)),
("Index", self._index_set if self.is_indexed() else None),
],
self._data.items(),
self.items,
("Name", "Size", "Variable"),
_line_generator,
)
Expand Down
2 changes: 1 addition & 1 deletion pyomo/core/base/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ def _pprint(self):
("Index", self._index_set if self.is_indexed() else None),
("Active", self.active),
],
self.items(),
self.items,
("Lower", "Body", "Upper", "Active"),
lambda k, v: [
"-Inf" if v.lower is None else v.lower,
Expand Down
2 changes: 1 addition & 1 deletion pyomo/core/base/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def _pprint(self):
('Size', len(self)),
('Index', None if (not self.is_indexed()) else self._index_set),
],
self.items(),
self.items,
("Expression",),
lambda k, v: ["Undefined" if v.expr is None else v.expr],
)
Expand Down
4 changes: 2 additions & 2 deletions pyomo/core/base/external.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ def _pprint(self):
),
),
],
(),
None,
None,
None,
)
Expand Down Expand Up @@ -614,7 +614,7 @@ def _pprint(self):
),
),
],
(),
None,
None,
None,
)
Expand Down
Loading