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

Revision 1992, 15.1 KB checked in by wehart, 4 years ago (diff)

A rework of component management in Pyomo. This was initially
started as an effort to add the component activate()/deactivate()
methods. However, this naturally led to a rework of
component management that I've been planning for a while.

Component types are now managed with the natural class types (e.g.
Var for variables, rather than _VarBase). Further, the components
should now be accessed with model methods: components() or active_components().
For example,

model.components(Var)

returns a dictionary of the Var components.

NOTE:

  1. I suspect that this commit will break PySP to the extent that it

uses the model._component data structure.

  1. This adds support for component-level deactivation, but not

component index deactivation.

  1. I haven't added unit tests to validate this type of deactivation,

but all of the standard unit tests work.

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