source: coopr.pyomo/trunk/coopr/pyomo/base/param.py @ 2046

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

Resolving #3970. When a parameter is specified with a default
value, we should be able to refer to its index set directly, since
all values are tacitly defined.

File size: 13.7 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__ = ['_ParamBase', 'Param']
12
13from numvalue import create_name
14from coopr.pyomo.base.numvalue import *
15from coopr.pyomo.base.plugin import ComponentRegistration
16from sets import _BaseSet
17from set_types import *
18import pyomo
19import sys
20import types
21from indexed_component import IndexedComponent
22
23
24class _ParamValue(NumericConstant):
25    """Holds the numeric value of a parameter"""
26
27    """Constructor"""
28    def __init__(self, **kwds):
29        try:
30            NumericConstant.__init__(self, **kwds)
31        except ValueError, e:
32            raise ValueError, "Problem constructing parameter "+self.name+" : "+str(e)
33
34    def fixed_value(self):
35        return True
36
37    def __str__(self):
38        return self.name
39
40
41class _ParamBase(IndexedComponent):
42    """A parameter value, which may be defined over a index"""
43
44    """ Constructor
45        Arguments:
46           name                The name of this parameter
47           index        The index set that defines the distinct parameters.
48                          By default, this is None, indicating that there
49                          is a single parameter.
50           within        A set that defines the type of values that
51                          each parameter must be.
52           validate     A rule for validating this parameter w.r.t. data
53                          that exists in the model
54           default        A set that defines default values for this
55                          parameter
56           rule                A rule for setting up this parameter with
57                          existing model data
58    """
59    def __init__(self, *args, **kwd):
60        tkwd = {'ctype':Param}
61        IndexedComponent.__init__(self, *args, **tkwd)
62        tmpname="unknown"
63        tmpdomain=Any
64        self._paramval={}
65        self._initialize={}
66        self._validate=None
67        defaultval=None
68        for key in kwd.keys():
69          if key == "rule":
70             self.__dict__["_initialize"] = kwd[key]
71          elif key is "name":
72                tmpname = kwd[key]
73          elif key == "doc":
74             self.doc=kwd[key]
75          elif key == "within":
76                tmpdomain = kwd[key]
77          elif key in ("validate","initialize"):
78             self.__dict__["_"+key] = kwd[key]
79          elif key == "default":
80             defaultval = kwd[key]
81          else:
82             raise ValueError, "Param constructor: unknown keyword - "+key
83        self.name = tmpname
84        self.domain = tmpdomain
85        self._default = _ParamValue(name=tmpname,domain=tmpdomain,value=value(defaultval))
86
87    def pprint(self, ostream=None):
88        if ostream is None:
89           ostream = sys.stdout
90        print >>ostream, "  ",self.name,":",
91        print >>ostream, "\tSize="+str(len(self)),
92        print >>ostream, "\tDomain="+self.domain.name
93        if None in self._paramval:
94           print >>ostream, "\t", self._paramval[None].value
95        else:
96           tmp=self._paramval.keys()
97           tmp.sort()
98           for key in tmp:
99             val = self._paramval[key].value
100             print >>ostream, "\t"+str(key)+" : "+str(val)
101        if self._default.value is not None and not self._default.value is None:
102           print >>ostream, "\tdefault: "+str(self._default.value)
103
104    def keys(self):
105        if self._default.value is None:
106            return self._paramval.keys()
107        else:
108            if not isinstance(self._index,_BaseSet):
109                raise ValueError, "Expected a set object for _index"
110            return self._index
111
112    def __contains__(self, element):
113        if self._default.value is None:
114            return element in self._paramval
115        else:
116            return element in self._index
117
118    def __iter__(self):
119        if self._default.value is None:
120            return self._paramval.keys().__iter__()
121        else:
122            return self._index.keys().__iter__()
123
124    def dim(self):
125        return self._ndim
126
127    def __len__(self):
128        if not self._default.value is None:
129           return len(self._index)
130        return len(self._paramval)
131
132    def __getitem__(self,ndx):
133        """This method returns a _ParamVal object.  This object can be
134           coerced to a numeric value using the value() function, or using
135           explicity coercion with float().
136        """
137        if ndx in self._paramval:
138           return self._paramval[ndx]
139        if ndx in self._index and self._default.value is not None and not self._default.value is None:
140           return self._default.value
141        if ndx in self._index:
142           errmsg = "Valid index " + str(ndx) + " for parameter " + self.name+" but no default value specified"
143           if pyomo.debug("verbose"):       #pragma:nocover
144              errmsg = errmsg+" : Defined indices="+str(self._paramval.keys())
145        else:
146           errmsg = "Unknown index " + str(ndx) + " in parameter " + self.name
147           if pyomo.debug("verbose"):       #pragma:nocover
148              errmsg = errmsg+" : Valid indices="+str(list(self._index.value))
149        raise KeyError, "Unknown index " + str(ndx) + " in parameter " + self.name
150
151    def __setitem__(self,ndx,val):
152
153        if self._index_set is not None:
154           # this is a "hack", in that we still need to check for legal sub-components in the input index.
155           if ndx in self._paramval:
156                self._paramval[ndx].value = val
157           else:
158                self._paramval[ndx]= _ParamValue(name=create_name(self.name,ndx),domain=self.domain,value=val)
159           return
160
161        if None in self._index:
162           raise KeyError, "Cannot set an array value in the simple parameter "+self.name
163        if ndx not in self._index:
164           raise KeyError, "Cannot set the value of array parameter "+self.name+" with invalid index "+str(ndx)
165        if ndx in self._paramval:
166           self._paramval[ndx].value = val
167        else:
168           self._paramval[ndx]= _ParamValue(name=create_name(self.name,ndx),domain=self.domain,value=val)           
169        if not self._valid_indexed_value(val,ndx,False):
170            raise ValueError, "Cannot set parameter "+self.name+" with invalid value: index=" + str(ndx) + " value=" + str(val)
171
172    def construct(self, data=None):
173        """ Apply the rule to construct values in this set """
174        if pyomo.debug("verbose"):      #pragma:nocover
175           print "Constructing Param, name="+self.name+", from data="+`data`
176        #
177        # Code optimization with local variables
178        #
179        _name=self.name
180        _domain=self.domain
181        #
182        # Update the default value, if it's available
183        #
184        try:
185            self._default.value = self.default
186        except:
187            pass
188        #
189        # Construct using the initial data or the data loaded from an
190        # external source.  Note:  data values will be queried in to following
191        # order:
192        #   1) the 'data' dictionary
193        #   2) the self._initialize dictionary
194        #   3) the default value
195        #
196        if data is not None or type(self._initialize) is not types.FunctionType:
197           #
198           # Singleton parameter
199           #
200           if type(self._index) is dict:
201              if data is not None and None in data.keys():
202                    self.value=data[None]
203              elif type(self._initialize) is not dict:
204                    self.value=self._initialize
205              elif None in self._initialize:
206                    self.value=self._initialize[None]
207              elif self._default.value is not None and not self._default.value is None:
208                    self.value=self._default.value
209                    #self._paramval[None] = self._default
210              else:
211                    raise ValueError, "Attempting to construct parameter "+_name+" without parameter data"
212              #print "Z",type(self._paramval[None].value), self._paramval[None].value
213              #print "Z",type(self.__call__())
214              self._valid_indexed_value(self.value,(),True)
215           else:
216              #
217              # Set external data values (if provided)
218              #
219              if data is not None:
220                 for key in data:
221                   if type(key) is tuple and len(key)==1:
222                      tmpkey=key[0]
223                   else:
224                      tmpkey=key
225                   self._paramval[tmpkey] = _ParamValue(name=create_name(_name,key),domain=_domain,value=data[key])
226                   self._valid_indexed_value(data[key],key,True)
227              #
228              # Initialize with initialization data.
229              #
230              elif self._initialize is not None:
231                 if type(self._initialize) is dict:
232                    for key in self._initialize:
233                      self._paramval[key] = _ParamValue(name=create_name(_name,key),domain=_domain,value=self._initialize[key])
234                      self._valid_indexed_value(self._initialize[key],key,True)
235                 else:
236                    for key in self._index:
237                      self._paramval[key] = _ParamValue(name=create_name(_name,key),domain=_domain,value=self._initialize)
238                    self._valid_indexed_value(self._initialize,(),True)
239        #
240        # Construct using the rule
241        #
242        elif type(self._initialize) is types.FunctionType:
243           if (type(self._index) is dict) and (None in self._index):
244              # singleton
245              tmp = []
246              tmp.append(self.model)
247              tval = self._initialize(*tmp)
248              self.value = tval
249           else:
250              # non-singleton
251              for val in self._index:
252                 if isinstance(val,tuple):
253                    tmp = list(val)
254                 elif val is None:
255                    tmp = []
256                 else:
257                    tmp = [val]
258                 if len(tmp) > 0:
259                       tname=create_name(_name,val)
260                 else:
261                       tname=_name
262                 tmp.append(self.model)
263                 tmp = tuple(tmp)
264                 tval = self._initialize(*tmp)
265                 self._paramval[val] = _ParamValue(name=tname,domain=_domain,value=tval)
266                 self._valid_indexed_value(tval,val,True)
267
268    def _valid_indexed_value(self,value,index, use_exception):
269        if self._validate is not None:
270           tmp = [value]
271           if index == ():
272              index = (None,)*self._ndim
273           if index is not None:
274              tmp = tmp + list(index)
275           tmp.append(self.model)
276           tmp = tuple(tmp)
277           if self._validate(*tmp):
278              return True
279        elif self.domain is None or value in self.domain:
280           return True
281        if use_exception:           #pragma:nocover
282           raise ValueError, "Invalid value "+str(value)+" for parameter "+self.name
283        return False
284       
285
286class _ParamElement(_ParamBase,_ParamValue):
287
288    def __init__(self, *args, **kwd):
289        _ParamValue.__init__(self, **kwd)
290        _ParamBase.__init__(self, *args, **kwd)
291        self._paramval[None] = self
292
293    #def simplify(self, model):
294        #return self
295        #return NumericConstant(value=self.value)
296
297    def check_values(self):         #pragma:nocover
298        #
299        # Validate the values
300        #
301        if None not in self._index:
302            raise ValueError, "Undefined value for parameter "+self.name
303        if not self._valid_indexed_value(self.value,None,False):
304            print "BUG",self.value,self._index
305            raise ValueError, "Parameter "+self.name+" failed validation test value=" + str(self.value)
306
307    def __call__(self, exception=True):
308        return self._paramval[None].value
309
310
311class _ParamArray(_ParamBase):
312
313    def __init__(self, *args, **kwd):
314        _ParamBase.__init__(self, *args, **kwd)
315
316    def simplify(self, model):
317        raise ValueError, "Cannot simplify array parameter "+self.name
318
319    def __float__(self):
320        raise ValueError, "Cannot access the value of array parameter "+self.name
321
322    def __int__(self):
323        raise ValueError, "Cannot access the value of array parameter "+self.name
324
325    def set_value(self, value):
326        for key in self._paramval:
327            self._paramval[key].value = value
328            self._valid_indexed_value(value,key,True)
329            #self._valid_value(value,key,True)
330
331    def check_values(self):         #pragma:nocover
332        #
333        # Validate the values
334        #
335        for val in self._paramval:
336            if val not in self._index:
337                raise ValueError, "Undefined value for parameter "+self.name+" index=" + str(val)
338            if not self._valid_indexed_value(self._paramval[val].value,val,False):
339                raise ValueError, "Parameter "+self.name+" failed validation test: index=" + str(val) + " value=" + str(self._paramval[val].value)
340
341    def reset(self):
342        pass                        #pragma:nocover
343
344    def display(self, ostream=None):
345        self.pprint(ostream=ostream)
346
347    def __str__(self):
348        return str(self.name)
349
350
351class Param(object):
352    """
353    Data objects that are used to construct Pyomo models.
354    """
355    def __new__(cls, *args, **kwds):
356        if args == ():
357            self = _ParamElement(*args, **kwds)
358        else:
359            self = _ParamArray(*args, **kwds)
360        return self
361
362
363ComponentRegistration("Param", Param, "Parameter data that is used to define a model instance.")
Note: See TracBrowser for help on using the repository browser.