source: trunk/coopr/pyomo/base/PyomoModel.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.

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