source: coopr.pyomo/trunk/coopr/pyomo/base/PyomoModel.py @ 1967

Revision 1967, 14.9 KB checked in by wehart, 4 years ago (diff)

Creating the ComponentBase? class, which manages the initialization of
components. This consolidates the mechanism needed to generalize
the component indexing operations.

  • Property svn:executable set to *
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
11import copy
12import re
13import sys
14
15from plugin import *
16from numvalue import *
17
18import pyutilib.plugin.core
19from pyutilib.math import *
20from pyutilib.misc import quote_split, tuplize
21
22from coopr.opt import ProblemFormat, ResultsFormat, guess_format
23from coopr.pyomo.base.var import _VarValue
24import coopr.opt
25from PyomoModelData import ModelData
26import pyomo
27
28from sets import Set, _ProductSet, _SetContainer, _BaseSet
29from rangeset import RangeSet
30from var import _VarBase
31from constraint import Objective, Constraint
32from param import _ParamBase
33
34
35class Model(object):
36    """
37    The MINLP model that will be analyzed by the user.
38    """
39
40    presolver_ep = pyutilib.plugin.core.ExtensionPoint(IPyomoPresolver)
41
42
43    def __init__(self):
44        """Constructor"""
45        #
46        # Define component dictionary: component type -> instance
47        #
48        self._component={}
49        for item in pyutilib.plugin.core.ExtensionPoint(IModelComponent):
50            self._component[ item.cls() ] = {}
51        #
52        # A list of the declarations, in the order that they are
53        # specified.
54        #
55        self._declarations=[]
56        # the _name_varmap is assigned when the problem definition
57        # file is written, i.e., via the write() command. intent
58        # is to map names that will be coming back from a solver
59        # into Pyomo variables. Values are of type _VarValue.
60        self._name_varmap={}
61        self.name="unknown"
62        self.tmpctr=0
63        self._varctr=0
64        #self._varmap={}
65        self._presolvers = ["simple_presolver"]
66        #
67        # Dictionaries that map from id to Variable/Constraints
68        #
69        self._var = {}
70        self._con = {}
71        self._obj = {}
72
73
74    def num_used_variables(self):
75        return len(self._var)
76
77
78    def valid_problem_types(self):
79        return [ProblemFormat.pyomo]
80
81
82    def _add_temporary_set(self,val):
83        if val._index_set is not None:
84            ctr=0
85            for tset in val._index_set:
86                if tset.name == "_unknown_":
87                    self._construct_temporary_set(tset,val.name+"_index_"+str(ctr))
88                ctr+=1
89            val._index = self._construct_temporary_set(val._index_set,val.name+"_index")
90        if isinstance(val._index,_SetContainer) and \
91            val._index.name == "_unknown_":
92            self._construct_temporary_set(val._index,val.name+"_index")
93        if val.domain is not None and val.domain.name == "_unknown_":
94            self._construct_temporary_set(val.domain,val.name+"_domain")
95
96
97    def _construct_temporary_set(self, obj, name):
98        if type(obj) is tuple:
99           if len(obj) == 1:                #pragma:nocover
100                  raise Exception, "Unexpected temporary set construction"
101           else:
102                  tobj=_ProductSet(*obj)
103                  setattr(self,name,tobj)
104                  tobj.virtual=True
105                  return tobj
106        if isinstance(obj,_BaseSet):
107           setattr(self,name,obj)
108           return obj
109
110
111    def _clear_attribute(self,name):
112        #
113        # Cleanup Old Model Declaration First
114        #
115        i=0
116        for decl in self._declarations:
117            if decl.name == name:
118                self.__dict__[name]=None
119                del self._declarations[i]
120                for item in self._component:
121                    if name in self._component[item]:
122                        del self._component[item][name]
123                        break
124                break
125            i += 1
126
127    def _setattr_exec(self,name,val,item):
128        self._clear_attribute(name)
129        val.name=name
130        self._add_temporary_set(val)
131        val.model=self
132        self._component[item][name]=val
133        self._declarations.append(val)
134        self.__dict__[name]=val
135
136    def __setattr__(self,name,val):
137        """Set attributes"""
138        #
139        # Set Model Declaration
140        #
141        if name != "_component":
142            #
143            # If this is a component type, then simply set it
144            #
145            if type(val) in self._component:
146                self._setattr_exec(name,val, type(val))
147                return
148            #
149            # Otherwise, see if this is an instance of a component type
150            #
151            for item in self._component:
152                #print "HERE z",name,val,item,isinstance(val,item)
153                if isinstance(val,item):
154                    self._setattr_exec(name,val, item)
155                    return
156
157        #if name in self.__dict__.keys() and isinstance(self.__dict__[name],NumericValue):
158            #tmpval = value(val)
159            #if tmpval is None:
160                #raise ValueError, "Bad numeric value"
161            #self.__dict__[name].value = tmpval
162        #else:
163            #self.__dict__[name]=val
164
165        #
166        # Try to set the value.  This may fail if the attribute does not already
167        # exist in this model, if the set_value function is not defined, or if
168        # a bad value is provided.  In the latter case, a ValueError will be thrown, which
169        # we raise.  Otherwise, this is an object that we need to set directly.
170        #
171        #print "HERE X",name
172        try:
173            #print "HERE",self.__dict__[name].name,type(self.__dict__[name]),type(val)
174            self.__dict__[name].set_value(val)
175        except ValueError, e:
176            raise
177        except Exception, e:
178            self.__dict__[name]=val
179
180
181    def create(self,filename=None):
182        """
183        Create a concrete instance of this Model, possibly using data
184        read in from a file.
185        """
186        instance = self.clone()
187        instance.load(filename)
188        instance.presolve()
189        return instance
190
191
192    def clone(self):
193        """Create a copy of this model"""
194        for declaration in self._declarations:
195          declaration.model = None
196        instance = copy.deepcopy(self)
197        for declaration in self._declarations:
198          declaration.model = self
199        for declaration in instance._declarations:
200          declaration.model = instance
201        return instance
202
203
204    def reset(self):
205        for declaration in self._declarations:
206            declaration.reset()
207
208
209    def presolve(self):
210        """Apply the presolvers defined by the user"""
211        for item in self._presolvers:
212            self = Model.presolver_ep.service(item).presolve(self)
213       
214
215    def solution(self):
216        """Return the solution"""
217        pass                            #pragma:nocover
218
219
220    def load(self,arg):
221        """ Load the model with data from a file or a Solution object """
222        if arg is None or type(arg) is str:
223           self._load_model_data(ModelData(filename=arg,model=self))
224           return True
225        elif type(arg) is ModelData:
226           self._load_model_data(arg)
227           return True
228        elif type(arg) is coopr.opt.SolverResults:
229           if arg.solver.status != coopr.opt.SolverStatus.ok:
230              raise ValueError, "Cannot load a SolverResults object with bad status: "+str(arg.solver.status)
231           if len(arg.solution) > 0:
232              self._load_solution(arg.solution(0),symbol_map=arg.symbol_map)
233              return True
234           else:
235              return False
236        elif type(arg) is coopr.opt.Solution:
237           self._load_solution(arg)
238           return True
239        else:
240           raise ValueError, "Cannot load model with object of type "+str(type(arg))
241
242
243    def _tuplize(self, data, setobj):
244        if data is None:            #pragma:nocover
245           return None
246        if setobj.dimen == 1:
247           return data
248        ans = {}
249        for key in data:
250          if type(data[key][0]) is tuple:
251             return data
252          ans[key] = tuplize(data[key], setobj.dimen, setobj.name)
253        return ans
254
255
256    def _load_model_data(self,modeldata):
257        """
258        Load declarations from a ModelData object.
259        """
260        #print "HERE _load_model_data",self._declarations
261        for declaration in self._declarations:
262          tmp = declaration.name
263          if tmp in modeldata._default.keys():
264             if isinstance(declaration,_BaseSet):
265                declaration.default = self._tuplize(modeldata._default[tmp],declaration)
266             else:
267                declaration.default = modeldata._default[tmp]
268          if tmp in modeldata._data.keys():
269             #print "HERE", declaration.name, str(declaration.dimen),modeldata._data[tmp]
270             if isinstance(declaration,_BaseSet):
271                data = self._tuplize(modeldata._data[tmp],declaration)
272             else:
273                data = modeldata._data[tmp]
274          else:
275             data = None
276          if pyomo.debug("generate"):           #pragma:nocover
277             print "About to generate '"+declaration.name+"' with data: "+str(data)
278             self.pprint()
279          declaration._construct(self,data)
280          try:
281            ##declaration._construct(self,data)
282            pass
283          except Exception, err:
284            print "Error constructing declaration "+str(declaration.name)+" from data="+str(data)
285            print "ERROR: "+str(err)
286            raise err
287
288    def _load_solution(self,soln, symbol_map=None):
289        """
290        Load a solution.
291        """
292        for name in soln.variable:
293          #
294          # NOTE: the following is a hack, to handle the ONE_VAR_CONSTANT variable
295          #       that is necessary for the objective constant-offset terms.
296          #       probably should create a dummy variable in the model map at the same
297          #       time the objective expression is being constructed.
298          #
299          if name != "ONE_VAR_CONSTANT":
300             # translate the name first if there is a variable map associated with the input solution.
301             if symbol_map is not None:
302                 name = symbol_map[name]
303             if name not in self._name_varmap:
304                names=""
305                raise KeyError, "Variable name '"+name+"' is not in model "+self.name+".  Valid variable names: "+str(self._name_varmap.keys())
306             if not isinstance(self._name_varmap[name],_VarValue):
307                raise TypeError, "Variable '"+name+"' in model '"+self.name+"' is type "+str(type(self._name_varmap[name]))
308             if self._name_varmap[name].fixed is True:
309                raise TypeError, "Variable '"+name+"' in model '"+self.name+"' is currently fixed - new value is not expected in solution"
310
311             # the value is always present in any legal solution.
312             self._name_varmap[name].value = soln.variable[name].value
313
314             # the reduced-cost might be present, depending on what the solver is reporting.
315             if hasattr(soln.variable[name],"rc"):
316                self._name_varmap[name].rc = soln.variable[name].rc
317
318    def write(self,filename=None,format=ProblemFormat.cpxlp):
319        """
320        Write the model to a file, with a given format.
321        """
322        if format is None and not filename is None:
323            #
324            # Guess the format
325            #
326            format = guess_format(filename)
327        problem_writer = coopr.opt.WriterFactory(format)
328        if problem_writer is None:
329           raise ValueError, "Cannot write model in format \"" + str(format) + "\": no model writer registered for that format"
330        (fname, symbol_map) = problem_writer(self, filename)
331        if pyomo.debug("verbose"):
332           print "Writing model "+self.name+" to file '"+str(fname)+"'  with format "+str(format)
333        return fname, symbol_map
334
335
336    def pprint(self, filename=None, ostream=None):
337        """
338        Print a summary of the model info
339        """
340        if ostream is None:
341           ostream = sys.stdout
342        if filename is not None:
343           OUTPUT=open(filename,"w")
344           self.pprint(ostream=OUTPUT)
345           OUTPUT.close()
346           return
347        if ostream is None:
348           ostream = sys.stdout
349        #
350        # We hard-code the order of the core Pyomo modeling
351        # components, to ensure that the output follows the logical
352        # expected by a user.
353        #
354        items = [_BaseSet, RangeSet, _ParamBase, _VarBase, Objective, Constraint]
355        for item in pyutilib.plugin.core.ExtensionPoint(IModelComponent):
356            if not item in items:
357                items.append(item)
358        for item in items:
359            if not item in self._component:
360                continue
361            keys = self._component[item].keys()
362            keys.sort()
363            #
364            # NOTE: these conditional checks should not be hard-coded.
365            #
366            if item.__name__ == "_BaseSet":
367                print >>ostream, len(keys), "Set Declarations"
368            elif item.__name__ == "_ParamBase":
369                print >>ostream, len(keys), "Param Declarations"
370            elif item.__name__ == "_VarBase":
371                print >>ostream, len(keys), "Var Declarations"
372            else:
373                print >>ostream, len(keys), item.__name__+" Declarations"
374            for key in keys:
375                self._component[item][key].pprint(ostream)
376            print >>ostream, ""
377        #
378        # Model Order
379        #
380        print >>ostream, len(self._declarations),"Declarations:",
381        for i in range(0,len(self._declarations)):
382          print >>ostream, self._declarations[i].name,
383        print >>ostream, ""
384
385
386    def display(self,filename=None, ostream=None):
387        """
388        Print the Pyomo model in a verbose format.
389        """
390        if filename is not None:
391           OUTPUT=open(filename,"w")
392           self.display(ostream=OUTPUT)
393           OUTPUT.close()
394           return
395        if ostream is None:
396           ostream = sys.stdout
397        print >>ostream, "Model "+self.name
398        print >>ostream, ""
399        print >>ostream, "  Variables:"
400        if len(self._component[_VarBase]) == 0:
401           print >>ostream, "    None"
402        else:
403           for ndx in self._component[_VarBase]:
404             self._component[_VarBase][ndx].display(prefix="    ",ostream=ostream)
405        print >>ostream, ""
406        print >>ostream, "  Objectives:"
407        if len(self._component[Objective]) == 0:
408           print >>ostream, "    None"
409        else:
410           for ndx in self._component[Objective]:
411             self._component[Objective][ndx].display(prefix="    ",ostream=ostream)
412        print >>ostream, ""
413        print >>ostream, "  Constraints:"
414        if len(self._component[Constraint]) == 0:
415           print >>ostream, "    None"
416        else:
417           for ndx in self._component[Constraint]:
418             self._component[Constraint][ndx].display(prefix="    ",ostream=ostream)
419
Note: See TracBrowser for help on using the repository browser.