source: coopr.pyomo/trunk/coopr/pyomo/base/numvalue.py @ 3071

Last change on this file since 3071 was 3071, checked in by wehart, 11 years ago

Bug fixes to param management.

File size: 10.5 KB
Line 
1#  _________________________________________________________________________
2#
3#  Coopr: A COmmon Optimization Python Repository
4#  Copyright (c) 2008 Sandia Corporation.
5#  This software is distributed under the BSD License.
6#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7#  the U.S. Government retains certain rights in this software.
8#  For more information, see the Coopr README.txt file.
9#  _________________________________________________________________________
10
11__all__ = ['value', 'NumericValue', 'as_numeric', 'NumericConstant', 'ZeroConstant', 'OneConstant', 'is_constant']
12
13import plugin
14#import pyutilib.component.core
15import pyutilib.math
16import sys
17from set_types import Reals
18
19
20##------------------------------------------------------------------------
21##
22## Standard types of expressions
23##
24##------------------------------------------------------------------------
25
26def create_name(name, ndx):
27    if ndx is None:
28        return name
29    if type(ndx) is tuple:
30        tmp = str(ndx).replace(', ',',')
31        return name+"["+tmp[1:-1]+"]"
32    return name+"["+str(ndx)+"]"
33
34def value(obj):
35    """
36    A utility function that returns the value of an expression
37        # NOTE: This is a terminal class, so no need to pop the keyword/argument
38        #       I would actually argue that we just need to make the inputs named
39        #       keywords, to by-pass all of this overhead.    """
40    if obj is None:
41        return None
42    elif type(obj) in [bool,int,long,float,str]:
43        return obj
44    if not isinstance(obj, NumericValue):
45        raise ValueError, "Object "+str(obj)+" is not a NumericValue object"
46    tmp = obj()
47    if tmp is None:
48        raise ValueError, "No value for uninitialized NumericValue object "+obj.name
49    return tmp
50
51def is_constant(obj):
52    """
53    A utility function that returns a boolean that indicates whether the object is a constant
54    """
55    if obj is None:
56        return False   
57    elif type(obj) in [bool,int,long,float]:
58        return True
59    if not isinstance(obj, NumericValue):
60        return False
61    return obj.is_constant()
62
63def as_numeric(obj):
64    """Verify that this obj is a NumericValue or intrinsic value"""
65    if isinstance(obj,NumericValue):
66       return obj.as_numeric()
67    if type(obj) in [bool,int,long,float]:
68       return NumericConstant(value=obj)
69    raise ValueError, "This object is not a numeric value: "+str(obj)
70
71
72class NumericValue(object):
73    """An object that contains a numeric value"""
74
75    __hash__ = None
76
77    def __init__(self, **kwargs):
78
79        self.name = 'unknown'
80        self.domain = None
81       
82        # a value of None indicates unassigned; None is also
83        # (treated as) a member of all domains.
84        value = None # validated prior to assigning to self.value
85
86        # because this is a terminal class (not passing keyword arguments
87        # further to the 'object' base class), there is a good reason in terms
88        # of performance - given how often NumericValue objects are created -
89        # to avoid the usual technique of invoking kwargs.pop(name,default).
90        # manipulating the keyword dictionary takes too much time.
91        for keyword, keywordvalue in kwargs.items():
92           if keyword is 'value':
93              value = keywordvalue           
94           elif keyword is 'within':
95              self.domain = keywordvalue
96           elif keyword is 'domain':
97              self.domain = keywordvalue             
98           elif keyword is 'name':
99              self.name = keywordvalue             
100           # this class can't deal with any other keywords - in principle,
101           # we could throw an exception for debug/diagnostic purposes.
102
103        if (value and pyutilib.math.is_nan(value)):
104            value = pyutilib.math.nan
105
106        # a numeric value is only constrained by the domain,
107        # if specified. avoid invoking set_value() here to
108        # accelerate object construction, which takes
109        # significant time due to the typical number of
110        # occurrences. similarly, we are avoiding the
111        # invocation of _valid_value.
112        if (value is not None) and (self.domain is not None) and (value not in self.domain):
113           raise ValueError, "Numeric value `"+str(value)+"` is not in domain "+str(self.domain)           
114
115        self.value = value
116       
117        self.model = None
118
119    def is_constant(self):
120        return True
121
122    def fixed_value(self):
123        return False
124
125    def as_numeric(self):
126        return self
127
128    def pprint(self, ostream=None):
129        raise IOError, "NumericValue:pprint is not defined"     #pragma:nocover
130
131    def display(self, ostream=None):
132        self.pprint(ostream=ostream)
133
134    def reset(self):            #pragma:nocover
135        pass                    #pragma:nocover
136
137    def set_value(self, val):
138        if self._valid_value(val):
139            self.value=val
140
141    def __call__(self, exception=True):
142        return self.value
143
144    def __float__(self):
145        tmp = self.__call__()
146        if tmp is None:
147            raise ValueError, "Cannot coerce variable `"+self.name+"' to float because it is uninitialized."
148        return float(tmp)
149
150    def __int__(self):
151        tmp = self.__call__()
152        if tmp is None:
153            raise ValueError, "Cannot coerce variable `"+self.name+"' to integer because it is uninitialized."
154        return int(tmp)
155
156    def _valid_value(self,value,use_exception=True):
157        #print "HERE X", self.domain is None
158        ans = value is None or self.domain is None or value in self.domain
159        if not ans and use_exception:
160           raise ValueError, "Numeric value `"+str(value)+"` is not in domain "+str(self.domain)
161        return ans
162
163    def X__getattr__(self,name):
164        #print "GETATTR",name
165        #d = self.__dict__
166        try:
167            return self.__dict__[name]
168        except:
169            pass
170        try:
171            return self.__dict__["_attr_"+name]
172        except:
173            pass
174        raise AttributeError, "Unknown attribute `"+str(name)+"' for object with type "+str(type(self))
175
176    def X__setattr__(self,name,val):
177        ##print "SETATTR",name,val
178        if name == "__class__":
179           self.__class__ = val
180           return
181        if name[0] == "_":
182           self.__dict__[name] = val
183           return
184        if name in self._standard_attr:
185           self.__dict__["_attr_"+name] = val
186           return
187        raise AttributeError, "Unallowable attribute `"+name+"`"
188        #self._standard_attr[name] = val
189
190    def __lt__(self,other):
191        """Less than operator
192
193        (Called in response to 'self < other' or 'other > self'.)
194        """
195        return plugin.ExpressionFactory('<', [self,as_numeric(other)])
196
197    def __gt__(self,other):
198        """Greater than operator
199
200        (Called in response to 'self > other' or 'other < self'.)
201        """
202        return plugin.ExpressionFactory('>', [self,as_numeric(other)])
203
204    def __le__(self,other):
205        """Less than or equal operator
206
207        (Called in response to 'self <= other' or 'other >= self'.)
208        """
209        return plugin.ExpressionFactory('<=', [self,as_numeric(other)])
210
211    def __ge__(self,other):
212        """Greater than or equal operator
213
214        (Called in response to 'self >= other' or 'other <= self'.)
215        """
216        return plugin.ExpressionFactory('>=', [self,as_numeric(other)])
217
218    def __eq__(self,other):
219        """Equal to operator
220
221        (Called in response to 'self = other'.)
222        """
223        return plugin.ExpressionFactory('=', [self,as_numeric(other)])
224
225    def __add__(self,other):
226        """Binary addition
227
228        (Called in response to 'self + other'.)
229        """
230        return expr.generate_expression('add',self,other)
231
232    def __sub__(self,other):
233        """ Binary subtraction
234
235        (Called in response to 'self - other'.)
236        """
237        return expr.generate_expression('sub',self,other)
238
239    def __mul__(self,other):
240        """ Binary multiplication
241
242        (Called in response to 'self * other'.)
243        """
244        return expr.generate_expression('mul',self,other)
245
246    def __div__(self,other):
247        """ Binary division
248
249        (Called in response to 'self / other'.)
250        """
251        return expr.generate_expression('div',self,other)
252
253    def __pow__(self,other):
254        """ Binary power
255
256        (Called in response to 'self ** other'.)
257        """
258        return expr.generate_expression('pow',self,other)
259
260    def __radd__(self,other):
261        """Binary addition
262
263        (Called in response to 'other + self'.)
264        """
265        return expr.generate_expression('radd',self,other)
266
267    def __rsub__(self,other):
268        """ Binary subtraction
269
270        (Called in response to 'other - self'.)
271        """
272        return expr.generate_expression('rsub',self,other)
273
274    def __rmul__(self,other):
275        """ Binary multiplication
276
277        (Called in response to 'other * self'.)
278        """
279        return expr.generate_expression('rmul',self,other)
280       
281    def __rdiv__(self,other):
282        """ Binary division
283
284        (Called in response to 'other / self'.)
285        """
286        return expr.generate_expression('rdiv',self,other)
287
288    def __rpow__(self,other):
289        """ Binary power
290
291        (Called in response to 'other ** self'.)
292        """
293        return expr.generate_expression('rpow',self,other)
294
295    def __neg__(self):
296        """ Negation
297
298        (Called in response to '- self'.)
299        """
300        return expr.generate_expression('neg',self)
301
302    def __pos__(self):
303        """ Positive expression
304
305        (Called in response to '+ self'.)
306        """
307        return self
308
309    def __abs__(self):
310        """ Absolute value
311
312        (Called in response to 'abs(self)'.)
313        """
314        return expr.generate_expression('abs',self)
315
316
317class NumericConstant(NumericValue):
318    """An object that contains a constant numeric value"""
319
320    def __init__(self, **kwds):
321        if not 'domain' in kwds:
322            kwds['domain'] = Reals
323        NumericValue.__init__(self,**kwds)
324
325    def fixed_value(self):
326        return True
327
328    def __call__(self, exception=True):
329        return self.value
330
331    def __str__(self):
332        return str(self.value)
333
334    def __eq__(self,other):
335        """Equal to operator
336
337        (Called in response to 'self == other'.)
338        """
339        return self.value == other
340
341    def pprint(self, ostream=None):
342        if ostream is None:
343           ostream = sys.stdout
344        print >>ostream, str(self),
345
346    def simplify(self, model):
347        return self                 #pragma:nocover
348
349
350ZeroConstant = NumericConstant(value=0)
351OneConstant = NumericConstant(value=1)
352
353import expr
Note: See TracBrowser for help on using the repository browser.