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

Last change on this file since 1772 was 1772, checked in by jwatson, 11 years ago

Updates to allow for proper handling of variable maps in cases where formats cannot handle fully qualified,
human-readable variable names. Many ripple effects, as problem writers now must return a variable map (or None,
if not applicable. CBC now properly handles and reads NL and SOL input/output combinations!

  • Property svn:executable set to *
File size: 14.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
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),variable_map=arg.variable_map)
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, variable_map=None):
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             # translate the name first if there is a variable map associated with the input solution.
303             if variable_map is not None:
304                 name = variable_map[name]
305             if name not in self._name_varmap:
306                names=""
307                raise KeyError, "Variable name '"+name+"' is not in model "+self.name+".  Valid variable names: "+str(self._name_varmap.keys())
308             if not isinstance(self._name_varmap[name],_VarValue):
309                raise TypeError, "Variable '"+name+"' in model '"+self.name+"' is type "+str(type(self._name_varmap[name]))
310             if self._name_varmap[name].fixed is True:
311                raise TypeError, "Variable '"+name+"' in model '"+self.name+"' is currently fixed - new value is not expected in solution"
312             self._name_varmap[name].value = value
313
314
315    def write(self,filename=None,format=ProblemFormat.cpxlp):
316        """
317        Write the model to a file, with a given format.
318        """
319        if format is None and not filename is None:
320            #
321            # Guess the format
322            #
323            for fmt in coopr.opt.ProblemFormat:
324                if filename.endswith('.'+str(fmt)):
325                    format=fmt
326                    break
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, variable_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, variable_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.