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

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

Adding documentation.

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