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

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

Misc bug fixes that were introduced by the introduction of variable_map
data, which is now called symbol_map.

Note: some tests still fail, due to the fact that pico_convert does not
generate symbol mapping information. This is being resolved.

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