Skip to content

Commit

Permalink
Merge pull request Pyomo#2444 from jsiirola/indexed-components-as-num…
Browse files Browse the repository at this point in the history
…eric

`as_numeric()` on a non-`is_numeric_type()` Pyomo Object should raise an exception
  • Loading branch information
jsiirola authored Jun 28, 2022
2 parents fb4236b + 1fd537d commit 5d47fbf
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 182 deletions.
102 changes: 59 additions & 43 deletions pyomo/core/expr/numvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def value(obj, exception=True):
# Test if we have a duck types for Pyomo expressions
#
try:
obj.is_expression_type()
obj.is_numeric_type()
except AttributeError:
#
# If not, then try to coerce this into a numeric constant. If that
Expand All @@ -137,7 +137,6 @@ def value(obj, exception=True):
#
# Here, we try to catch the exception
#

try:
tmp = obj(exception=True)
if tmp is None:
Expand Down Expand Up @@ -257,7 +256,7 @@ def is_numeric_data(obj):
# this likely means it is a string
return False
try:
# Test if this is an expression object that
# Test if this is an expression object that
# is not potentially variable
return not obj.is_potentially_variable()
except AttributeError:
Expand Down Expand Up @@ -330,15 +329,15 @@ def as_numeric(obj):
Args:
obj: The numeric value that may be wrapped.
Raises: TypeError if the object is in native_types and not in
Raises: TypeError if the object is in native_types and not in
native_numeric_types
Returns: A NumericConstant object or the original object.
"""
if obj.__class__ in native_numeric_types:
val = _KnownConstants.get(obj, None)
if val is not None:
return val
return val
#
# Coerce the value to a float, if possible
#
Expand Down Expand Up @@ -367,11 +366,20 @@ def as_numeric(obj):
#
return retval
#
# Ignore objects that are duck types to work with Pyomo expressions
# Ignore objects that are duck typed to work with Pyomo expressions
#
try:
obj.is_expression_type()
return obj
if obj.is_numeric_type():
return obj
else:
try:
_name = obj.name
except AttributeError:
_name = str(obj)
raise TypeError(
"The '%s' object '%s' is not a valid type for Pyomo "
"numeric expressions" % (type(obj).__name__, _name))

except AttributeError:
pass
#
Expand All @@ -386,10 +394,11 @@ def as_numeric(obj):
# Generate errors
#
if obj.__class__ in native_types:
raise TypeError("Cannot treat the value '%s' as a constant" % str(obj))
raise TypeError("%s values ('%s') are not allowed in Pyomo "
"numeric expressions" % (type(obj).__name__, str(obj)))
raise TypeError(
"Cannot treat the value '%s' as a constant because it has unknown "
"type '%s'" % (str(obj), type(obj).__name__))
"Cannot treat the value '%s' as a numeric value because it has "
"unknown type '%s'" % (str(obj), type(obj).__name__))


def check_if_numeric_type_and_cache(obj):
Expand All @@ -415,7 +424,7 @@ def check_if_numeric_type_and_cache(obj):
retval = NumericConstant(obj)
try:
#
# Create the numeric constant and add to the
# Create the numeric constant and add to the
# list of known constants.
#
# Note: we don't worry about the size of the
Expand Down Expand Up @@ -647,7 +656,7 @@ def __lt__(self,other):
Less than operator
This method is called when Python processes statements of the form::
self < other
other > self
"""
Expand All @@ -658,7 +667,7 @@ def __gt__(self,other):
Greater than operator
This method is called when Python processes statements of the form::
self > other
other < self
"""
Expand All @@ -669,7 +678,7 @@ def __le__(self,other):
Less than or equal operator
This method is called when Python processes statements of the form::
self <= other
other >= self
"""
Expand All @@ -680,7 +689,7 @@ def __ge__(self,other):
Greater than or equal operator
This method is called when Python processes statements of the form::
self >= other
other <= self
"""
Expand All @@ -691,7 +700,7 @@ def __eq__(self,other):
Equal to operator
This method is called when Python processes the statement::
self == other
"""
return _generate_relational_expression(_eq, self, other)
Expand All @@ -701,7 +710,7 @@ def __add__(self,other):
Binary addition
This method is called when Python processes the statement::
self + other
"""
return _generate_sum_expression(_add,self,other)
Expand All @@ -711,7 +720,7 @@ def __sub__(self,other):
Binary subtraction
This method is called when Python processes the statement::
self - other
"""
return _generate_sum_expression(_sub,self,other)
Expand All @@ -721,7 +730,7 @@ def __mul__(self,other):
Binary multiplication
This method is called when Python processes the statement::
self * other
"""
return _generate_mul_expression(_mul,self,other)
Expand All @@ -731,7 +740,7 @@ def __div__(self,other):
Binary division
This method is called when Python processes the statement::
self / other
"""
return _generate_mul_expression(_div,self,other)
Expand All @@ -741,7 +750,7 @@ def __truediv__(self,other):
Binary division (when __future__.division is in effect)
This method is called when Python processes the statement::
self / other
"""
return _generate_mul_expression(_div,self,other)
Expand All @@ -751,7 +760,7 @@ def __pow__(self,other):
Binary power
This method is called when Python processes the statement::
self ** other
"""
return _generate_other_expression(_pow,self,other)
Expand All @@ -761,7 +770,7 @@ def __radd__(self,other):
Binary addition
This method is called when Python processes the statement::
other + self
"""
return _generate_sum_expression(_radd,self,other)
Expand All @@ -771,7 +780,7 @@ def __rsub__(self,other):
Binary subtraction
This method is called when Python processes the statement::
other - self
"""
return _generate_sum_expression(_rsub,self,other)
Expand All @@ -781,7 +790,7 @@ def __rmul__(self,other):
Binary multiplication
This method is called when Python processes the statement::
other * self
when other is not a :class:`NumericValue <pyomo.core.expr.numvalue.NumericValue>` object.
Expand All @@ -792,7 +801,7 @@ def __rdiv__(self,other):
"""Binary division
This method is called when Python processes the statement::
other / self
"""
return _generate_mul_expression(_rdiv,self,other)
Expand All @@ -802,7 +811,7 @@ def __rtruediv__(self,other):
Binary division (when __future__.division is in effect)
This method is called when Python processes the statement::
other / self
"""
return _generate_mul_expression(_rdiv,self,other)
Expand All @@ -812,7 +821,7 @@ def __rpow__(self,other):
Binary power
This method is called when Python processes the statement::
other ** self
"""
return _generate_other_expression(_rpow,self,other)
Expand All @@ -822,7 +831,7 @@ def __iadd__(self,other):
Binary addition
This method is called when Python processes the statement::
self += other
"""
return _generate_sum_expression(_iadd,self,other)
Expand Down Expand Up @@ -852,7 +861,7 @@ def __idiv__(self,other):
Binary division
This method is called when Python processes the statement::
self /= other
"""
return _generate_mul_expression(_idiv,self,other)
Expand All @@ -862,7 +871,7 @@ def __itruediv__(self,other):
Binary division (when __future__.division is in effect)
This method is called when Python processes the statement::
self /= other
"""
return _generate_mul_expression(_idiv,self,other)
Expand All @@ -872,7 +881,7 @@ def __ipow__(self,other):
Binary power
This method is called when Python processes the statement::
self **= other
"""
return _generate_other_expression(_ipow,self,other)
Expand All @@ -882,7 +891,7 @@ def __neg__(self):
Negation
This method is called when Python processes the statement::
- self
"""
return _generate_sum_expression(_neg, self, None)
Expand All @@ -892,7 +901,7 @@ def __pos__(self):
Positive expression
This method is called when Python processes the statement::
+ self
"""
return self
Expand All @@ -901,7 +910,7 @@ def __abs__(self):
""" Absolute value
This method is called when Python processes the statement::
abs(self)
"""
return _generate_other_expression(_abs,self, None)
Expand All @@ -912,25 +921,32 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):

def to_string(self, verbose=None, labeler=None, smap=None,
compute_values=False):
"""
Return a string representation of the expression tree.
"""Return a string representation of the expression tree.
Args:
verbose (bool): If :const:`True`, then the the string
verbose (bool): If :const:`True`, then the string
representation consists of nested functions. Otherwise,
the string representation is an algebraic equation.
the string representation is an infix algebraic equation.
Defaults to :const:`False`.
labeler: An object that generates string labels for
variables in the expression tree. Defaults to :const:`None`.
labeler: An object that generates string labels for
non-constant in the expression tree. Defaults to
:const:`None`.
smap: A SymbolMap instance that stores string labels for
non-constant nodes in the expression tree. Defaults to
:const:`None`.
compute_values (bool): If :const:`True`, then fixed
expressions are evaluated and the string representation
of the resulting value is returned.
Returns:
A string representation for the expression tree.
"""
if compute_values and self.is_fixed():
try:
return str(self())
except:
pass
pass
if not self.is_constant():
if smap is not None:
return smap.getSymbol(self, labeler)
Expand Down
Loading

0 comments on commit 5d47fbf

Please sign in to comment.