Skip to content

Commit 5d47fbf

Browse files
authored
Merge pull request Pyomo#2444 from jsiirola/indexed-components-as-numeric
`as_numeric()` on a non-`is_numeric_type()` Pyomo Object should raise an exception
2 parents fb4236b + 1fd537d commit 5d47fbf

File tree

2 files changed

+126
-182
lines changed

2 files changed

+126
-182
lines changed

pyomo/core/expr/numvalue.py

+59-43
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def value(obj, exception=True):
117117
# Test if we have a duck types for Pyomo expressions
118118
#
119119
try:
120-
obj.is_expression_type()
120+
obj.is_numeric_type()
121121
except AttributeError:
122122
#
123123
# If not, then try to coerce this into a numeric constant. If that
@@ -137,7 +137,6 @@ def value(obj, exception=True):
137137
#
138138
# Here, we try to catch the exception
139139
#
140-
141140
try:
142141
tmp = obj(exception=True)
143142
if tmp is None:
@@ -257,7 +256,7 @@ def is_numeric_data(obj):
257256
# this likely means it is a string
258257
return False
259258
try:
260-
# Test if this is an expression object that
259+
# Test if this is an expression object that
261260
# is not potentially variable
262261
return not obj.is_potentially_variable()
263262
except AttributeError:
@@ -330,15 +329,15 @@ def as_numeric(obj):
330329
Args:
331330
obj: The numeric value that may be wrapped.
332331
333-
Raises: TypeError if the object is in native_types and not in
332+
Raises: TypeError if the object is in native_types and not in
334333
native_numeric_types
335334
336335
Returns: A NumericConstant object or the original object.
337336
"""
338337
if obj.__class__ in native_numeric_types:
339338
val = _KnownConstants.get(obj, None)
340339
if val is not None:
341-
return val
340+
return val
342341
#
343342
# Coerce the value to a float, if possible
344343
#
@@ -367,11 +366,20 @@ def as_numeric(obj):
367366
#
368367
return retval
369368
#
370-
# Ignore objects that are duck types to work with Pyomo expressions
369+
# Ignore objects that are duck typed to work with Pyomo expressions
371370
#
372371
try:
373-
obj.is_expression_type()
374-
return obj
372+
if obj.is_numeric_type():
373+
return obj
374+
else:
375+
try:
376+
_name = obj.name
377+
except AttributeError:
378+
_name = str(obj)
379+
raise TypeError(
380+
"The '%s' object '%s' is not a valid type for Pyomo "
381+
"numeric expressions" % (type(obj).__name__, _name))
382+
375383
except AttributeError:
376384
pass
377385
#
@@ -386,10 +394,11 @@ def as_numeric(obj):
386394
# Generate errors
387395
#
388396
if obj.__class__ in native_types:
389-
raise TypeError("Cannot treat the value '%s' as a constant" % str(obj))
397+
raise TypeError("%s values ('%s') are not allowed in Pyomo "
398+
"numeric expressions" % (type(obj).__name__, str(obj)))
390399
raise TypeError(
391-
"Cannot treat the value '%s' as a constant because it has unknown "
392-
"type '%s'" % (str(obj), type(obj).__name__))
400+
"Cannot treat the value '%s' as a numeric value because it has "
401+
"unknown type '%s'" % (str(obj), type(obj).__name__))
393402

394403

395404
def check_if_numeric_type_and_cache(obj):
@@ -415,7 +424,7 @@ def check_if_numeric_type_and_cache(obj):
415424
retval = NumericConstant(obj)
416425
try:
417426
#
418-
# Create the numeric constant and add to the
427+
# Create the numeric constant and add to the
419428
# list of known constants.
420429
#
421430
# Note: we don't worry about the size of the
@@ -647,7 +656,7 @@ def __lt__(self,other):
647656
Less than operator
648657
649658
This method is called when Python processes statements of the form::
650-
659+
651660
self < other
652661
other > self
653662
"""
@@ -658,7 +667,7 @@ def __gt__(self,other):
658667
Greater than operator
659668
660669
This method is called when Python processes statements of the form::
661-
670+
662671
self > other
663672
other < self
664673
"""
@@ -669,7 +678,7 @@ def __le__(self,other):
669678
Less than or equal operator
670679
671680
This method is called when Python processes statements of the form::
672-
681+
673682
self <= other
674683
other >= self
675684
"""
@@ -680,7 +689,7 @@ def __ge__(self,other):
680689
Greater than or equal operator
681690
682691
This method is called when Python processes statements of the form::
683-
692+
684693
self >= other
685694
other <= self
686695
"""
@@ -691,7 +700,7 @@ def __eq__(self,other):
691700
Equal to operator
692701
693702
This method is called when Python processes the statement::
694-
703+
695704
self == other
696705
"""
697706
return _generate_relational_expression(_eq, self, other)
@@ -701,7 +710,7 @@ def __add__(self,other):
701710
Binary addition
702711
703712
This method is called when Python processes the statement::
704-
713+
705714
self + other
706715
"""
707716
return _generate_sum_expression(_add,self,other)
@@ -711,7 +720,7 @@ def __sub__(self,other):
711720
Binary subtraction
712721
713722
This method is called when Python processes the statement::
714-
723+
715724
self - other
716725
"""
717726
return _generate_sum_expression(_sub,self,other)
@@ -721,7 +730,7 @@ def __mul__(self,other):
721730
Binary multiplication
722731
723732
This method is called when Python processes the statement::
724-
733+
725734
self * other
726735
"""
727736
return _generate_mul_expression(_mul,self,other)
@@ -731,7 +740,7 @@ def __div__(self,other):
731740
Binary division
732741
733742
This method is called when Python processes the statement::
734-
743+
735744
self / other
736745
"""
737746
return _generate_mul_expression(_div,self,other)
@@ -741,7 +750,7 @@ def __truediv__(self,other):
741750
Binary division (when __future__.division is in effect)
742751
743752
This method is called when Python processes the statement::
744-
753+
745754
self / other
746755
"""
747756
return _generate_mul_expression(_div,self,other)
@@ -751,7 +760,7 @@ def __pow__(self,other):
751760
Binary power
752761
753762
This method is called when Python processes the statement::
754-
763+
755764
self ** other
756765
"""
757766
return _generate_other_expression(_pow,self,other)
@@ -761,7 +770,7 @@ def __radd__(self,other):
761770
Binary addition
762771
763772
This method is called when Python processes the statement::
764-
773+
765774
other + self
766775
"""
767776
return _generate_sum_expression(_radd,self,other)
@@ -771,7 +780,7 @@ def __rsub__(self,other):
771780
Binary subtraction
772781
773782
This method is called when Python processes the statement::
774-
783+
775784
other - self
776785
"""
777786
return _generate_sum_expression(_rsub,self,other)
@@ -781,7 +790,7 @@ def __rmul__(self,other):
781790
Binary multiplication
782791
783792
This method is called when Python processes the statement::
784-
793+
785794
other * self
786795
787796
when other is not a :class:`NumericValue <pyomo.core.expr.numvalue.NumericValue>` object.
@@ -792,7 +801,7 @@ def __rdiv__(self,other):
792801
"""Binary division
793802
794803
This method is called when Python processes the statement::
795-
804+
796805
other / self
797806
"""
798807
return _generate_mul_expression(_rdiv,self,other)
@@ -802,7 +811,7 @@ def __rtruediv__(self,other):
802811
Binary division (when __future__.division is in effect)
803812
804813
This method is called when Python processes the statement::
805-
814+
806815
other / self
807816
"""
808817
return _generate_mul_expression(_rdiv,self,other)
@@ -812,7 +821,7 @@ def __rpow__(self,other):
812821
Binary power
813822
814823
This method is called when Python processes the statement::
815-
824+
816825
other ** self
817826
"""
818827
return _generate_other_expression(_rpow,self,other)
@@ -822,7 +831,7 @@ def __iadd__(self,other):
822831
Binary addition
823832
824833
This method is called when Python processes the statement::
825-
834+
826835
self += other
827836
"""
828837
return _generate_sum_expression(_iadd,self,other)
@@ -852,7 +861,7 @@ def __idiv__(self,other):
852861
Binary division
853862
854863
This method is called when Python processes the statement::
855-
864+
856865
self /= other
857866
"""
858867
return _generate_mul_expression(_idiv,self,other)
@@ -862,7 +871,7 @@ def __itruediv__(self,other):
862871
Binary division (when __future__.division is in effect)
863872
864873
This method is called when Python processes the statement::
865-
874+
866875
self /= other
867876
"""
868877
return _generate_mul_expression(_idiv,self,other)
@@ -872,7 +881,7 @@ def __ipow__(self,other):
872881
Binary power
873882
874883
This method is called when Python processes the statement::
875-
884+
876885
self **= other
877886
"""
878887
return _generate_other_expression(_ipow,self,other)
@@ -882,7 +891,7 @@ def __neg__(self):
882891
Negation
883892
884893
This method is called when Python processes the statement::
885-
894+
886895
- self
887896
"""
888897
return _generate_sum_expression(_neg, self, None)
@@ -892,7 +901,7 @@ def __pos__(self):
892901
Positive expression
893902
894903
This method is called when Python processes the statement::
895-
904+
896905
+ self
897906
"""
898907
return self
@@ -901,7 +910,7 @@ def __abs__(self):
901910
""" Absolute value
902911
903912
This method is called when Python processes the statement::
904-
913+
905914
abs(self)
906915
"""
907916
return _generate_other_expression(_abs,self, None)
@@ -912,25 +921,32 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
912921

913922
def to_string(self, verbose=None, labeler=None, smap=None,
914923
compute_values=False):
915-
"""
916-
Return a string representation of the expression tree.
924+
"""Return a string representation of the expression tree.
917925
918926
Args:
919-
verbose (bool): If :const:`True`, then the the string
927+
verbose (bool): If :const:`True`, then the string
920928
representation consists of nested functions. Otherwise,
921-
the string representation is an algebraic equation.
929+
the string representation is an infix algebraic equation.
922930
Defaults to :const:`False`.
923-
labeler: An object that generates string labels for
924-
variables in the expression tree. Defaults to :const:`None`.
931+
labeler: An object that generates string labels for
932+
non-constant in the expression tree. Defaults to
933+
:const:`None`.
934+
smap: A SymbolMap instance that stores string labels for
935+
non-constant nodes in the expression tree. Defaults to
936+
:const:`None`.
937+
compute_values (bool): If :const:`True`, then fixed
938+
expressions are evaluated and the string representation
939+
of the resulting value is returned.
925940
926941
Returns:
927942
A string representation for the expression tree.
943+
928944
"""
929945
if compute_values and self.is_fixed():
930946
try:
931947
return str(self())
932948
except:
933-
pass
949+
pass
934950
if not self.is_constant():
935951
if smap is not None:
936952
return smap.getSymbol(self, labeler)

0 commit comments

Comments
 (0)