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

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

Rework of Coopr to use the new PyUtilib? package decomposition.

NOTE: to use Coopr with this update, we need to work with a new version of coopr_install.

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