Pyomo / pyomo

An object-oriented algebraic modeling language in Python for structured optimization problems.
https://www.pyomo.org
Other
2.02k stars 518 forks source link

Printing numbers with units #2935

Closed codykarcher closed 1 year ago

codykarcher commented 1 year ago

Summary

Printing numbers that have units can be extremely unhelpful at times

from pyomo.environ import units
print(2.0*units.m)
# 2.0*m
print([2.0*units.m])
# [<pyomo.core.expr.numeric_expr.NPV_ProductExpression object at 0x105db5850>]

Rationale

At first this was a minor inconvenience, but is now becoming extremely annoying/debilitating as these things get stored in progressively larger and larger objects

Description

Consider in the print statement of NPV_ProductExpression to see if it is a number times a unit, and if so, printing the equivalent of a pint quantity

Additional information

N/A

jsiirola commented 1 year ago

Unfortunately, this is intentional behavior.

Per the Python docs, repr() should:

Return a string containing a printable representation of an object. For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(); otherwise, the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object.

Because Pyomo objects require a context to be created (the model or the current UnitsManager), it it not (reliably) possible to generate strings that can be eval()'ed back into Pyomo objects. Since that is the case, the correct thing to return is the class name and address.

In my opinion, I think this exposes a bug in Python's implementation of list.__str__ (and tuple.__str__ and dict.__str__): they call repr() on the contained objects and not str(). Pyomo has a "workaround" implementation in the pyomo.common.formatting.tostr() function. This attempts to (recursively) call str() on the contained objects:

>>> from pyomo.common.formatting import tostr
>>> tostr([2.0*units.m])
'[2.0*m]'
codykarcher commented 1 year ago

This solution had the desired effect.